From 45dae5ce43fbfbdf1caa26a22c8af8a76c46be0e Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Sun, 20 Mar 2022 22:00:21 -0400
Subject: [PATCH] Add support for viewer-side baking

---
 indra/llappearance/llavatarappearance.cpp     |  11 +-
 indra/llappearance/llavatarappearance.h       |  24 +-
 indra/llappearance/lldriverparam.cpp          |  28 +-
 indra/llappearance/lldriverparam.h            |  34 +-
 indra/llappearance/llpolymorph.cpp            |   2 +-
 .../llappearance/llpolyskeletaldistortion.cpp |   2 +-
 indra/llappearance/lltexglobalcolor.cpp       |   4 +-
 indra/llappearance/lltexglobalcolor.h         |   4 +-
 indra/llappearance/lltexlayer.cpp             |   6 +-
 indra/llappearance/lltexlayer.h               |   2 +-
 indra/llappearance/lltexlayerparams.cpp       |  32 +-
 indra/llappearance/lltexlayerparams.h         |  48 +-
 indra/llappearance/llviewervisualparam.cpp    |   2 +-
 indra/llappearance/llwearable.cpp             |  18 +-
 indra/llappearance/llwearable.h               |   6 +-
 indra/llcharacter/llcharacter.cpp             |  14 +-
 indra/llcharacter/llcharacter.h               |   7 +-
 indra/llcharacter/llvisualparam.cpp           |  18 +-
 indra/llcharacter/llvisualparam.h             |   8 +-
 indra/newview/CMakeLists.txt                  |   2 +
 indra/newview/llagent.cpp                     | 333 +++++++++-
 indra/newview/llagent.h                       |  25 +
 indra/newview/llagentwearables.cpp            | 448 +++++++++++++-
 indra/newview/llagentwearables.h              |  45 +-
 indra/newview/llagentwearablesfetch.cpp       | 585 ++++++++++++++++++
 indra/newview/llagentwearablesfetch.h         | 114 ++++
 indra/newview/llappearancemgr.cpp             |   2 +-
 indra/newview/llemote.cpp                     |  12 +-
 indra/newview/lllocalbitmaps.cpp              |   2 +-
 indra/newview/llpaneleditwearable.cpp         |  20 +-
 indra/newview/llphysicsmotion.cpp             |   4 +-
 indra/newview/llscrollingpanelparam.cpp       |   6 +-
 indra/newview/llscrollingpanelparambase.cpp   |   2 +-
 indra/newview/llstartup.cpp                   |  30 +-
 indra/newview/lltextureview.cpp               |   9 +
 indra/newview/lltoolmorph.cpp                 |   6 +-
 indra/newview/llviewermessage.cpp             |   4 +
 indra/newview/llviewerobject.cpp              |   2 +-
 indra/newview/llviewerregion.cpp              |   2 +-
 indra/newview/llviewertexlayer.cpp            | 490 ++++++++++++++-
 indra/newview/llviewertexlayer.h              |  81 ++-
 indra/newview/llviewertexture.cpp             |   3 +-
 indra/newview/llviewerwearable.cpp            |  16 +-
 indra/newview/llviewerwearable.h              |   6 +-
 indra/newview/llvoavatar.cpp                  | 201 ++++--
 indra/newview/llvoavatar.h                    |  29 +-
 indra/newview/llvoavatarself.cpp              | 342 ++++++++--
 indra/newview/llvoavatarself.h                |  77 ++-
 48 files changed, 2810 insertions(+), 358 deletions(-)
 create mode 100644 indra/newview/llagentwearablesfetch.cpp
 create mode 100644 indra/newview/llagentwearablesfetch.h

diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
index cdee35d9f65..7943c96d643 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -560,6 +560,7 @@ void LLAvatarAppearance::computeBodySize()
 #ifdef SHOW_DEBUG
         compareJointStateMaps(mLastBodySizeState, mCurrBodySizeState);
 #endif
+		bodySizeChanged();
 	}
 }
 
@@ -1494,14 +1495,14 @@ BOOL LLAvatarAppearance::teToColorParams( ETextureIndex te, U32 *param_name )
 	return TRUE;
 }
 
-void LLAvatarAppearance::setClothesColor( ETextureIndex te, const LLColor4& new_color)
+void LLAvatarAppearance::setClothesColor( ETextureIndex te, const LLColor4& new_color, BOOL upload_bake )
 {
 	U32 param_name[3];
 	if( teToColorParams( te, param_name ) )
 	{
-		setVisualParamWeight( param_name[0], new_color.mV[VX]);
-		setVisualParamWeight( param_name[1], new_color.mV[VY]);
-		setVisualParamWeight( param_name[2], new_color.mV[VZ]);
+		setVisualParamWeight( param_name[0], new_color.mV[VX], upload_bake );
+		setVisualParamWeight( param_name[1], new_color.mV[VY], upload_bake );
+		setVisualParamWeight( param_name[2], new_color.mV[VZ], upload_bake );
 	}
 }
 
@@ -2143,7 +2144,7 @@ BOOL LLAvatarAppearance::LLAvatarXmlInfo::parseXmlMorphNodes(LLXmlTreeNode* root
 LLAvatarAppearance::LLMaskedMorph::LLMaskedMorph(LLVisualParam *morph_target, BOOL invert, std::string layer) :
 			mMorphTarget(morph_target), 
 			mInvert(invert),
-			mLayer(layer)
+			mLayer(std::move(layer))
 {
 	LLPolyMorphTarget *target = dynamic_cast<LLPolyMorphTarget*>(morph_target);
 	if (target)
diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h
index c721c86dd8e..6d2f720b93c 100644
--- a/indra/llappearance/llavatarappearance.h
+++ b/indra/llappearance/llavatarappearance.h
@@ -90,14 +90,14 @@ class LLAvatarAppearance : public LLCharacter
 	// LLCharacter interface and related
 	//--------------------------------------------------------------------
 public:
-	/*virtual*/ LLJoint*		getCharacterJoint(U32 num);
+	/*virtual*/ LLJoint*		getCharacterJoint(U32 num) override;
 
-	/*virtual*/ const char*		getAnimationPrefix() { return "avatar"; }
-	/*virtual*/ LLVector3		getVolumePos(S32 joint_index, LLVector3& volume_offset);
-	/*virtual*/ LLJoint*		findCollisionVolume(S32 volume_id);
-	/*virtual*/ S32				getCollisionVolumeID(std::string_view name);
-	/*virtual*/ LLPolyMesh*		getHeadMesh();
-	/*virtual*/ LLPolyMesh*		getUpperBodyMesh();
+	/*virtual*/ const char*		getAnimationPrefix() override { return "avatar"; }
+	/*virtual*/ LLVector3		getVolumePos(S32 joint_index, LLVector3& volume_offset) override;
+	/*virtual*/ LLJoint*		findCollisionVolume(S32 volume_id) override;
+	/*virtual*/ S32				getCollisionVolumeID(std::string_view name) override;
+	/*virtual*/ LLPolyMesh*		getHeadMesh() override;
+	/*virtual*/ LLPolyMesh*		getUpperBodyMesh() override;
 
 /**                    Inherited
  **                                                                            **
@@ -110,6 +110,7 @@ class LLAvatarAppearance : public LLCharacter
 public:
 	virtual bool 	isSelf() const { return false; } // True if this avatar is for this viewer's agent
 	virtual BOOL	isValid() const;
+	virtual BOOL	isUsingServerBakes() const = 0;
 	virtual BOOL	isUsingLocalAppearance() const = 0;
 	virtual BOOL	isEditingAppearance() const = 0;
 
@@ -134,7 +135,7 @@ class LLAvatarAppearance : public LLCharacter
 
 public:
 	F32					getPelvisToFoot() const { return mPelvisToFoot; }
-	/*virtual*/ LLJoint*	getRootJoint() { return mRoot; }
+	/*virtual*/ LLJoint*	getRootJoint() override { return mRoot; }
 
 	LLVector3			mHeadOffset; // current head position
 	LLAvatarJoint		*mRoot;
@@ -160,6 +161,7 @@ class LLAvatarAppearance : public LLCharacter
 	static BOOL			parseSkeletonFile(const std::string& filename, LLXmlTree& skeleton_xml_tree);
 	virtual void		buildCharacter();
 	virtual BOOL		loadAvatar();
+	virtual void		bodySizeChanged() = 0;
 
 	BOOL				setupBone(const LLAvatarBoneInfo* info, LLJoint* parent, S32 &current_volume_num, S32 &current_joint_num);
 	BOOL				allocateCharacterJoints(U32 num);
@@ -243,7 +245,7 @@ class LLAvatarAppearance : public LLCharacter
 	// Composites
 	//--------------------------------------------------------------------
 public:
-	virtual void	invalidateComposite(LLTexLayerSet* layerset) = 0;
+	virtual void	invalidateComposite(LLTexLayerSet* layerset, BOOL upload_result) = 0;
 
 /********************************************************************************
  **                                                                            **
@@ -278,7 +280,7 @@ class LLAvatarAppearance : public LLCharacter
 	// Clothing colors (convenience functions to access visual parameters)
 	//--------------------------------------------------------------------
 public:
-	void			setClothesColor(LLAvatarAppearanceDefines::ETextureIndex te, const LLColor4& new_color);
+	void			setClothesColor(LLAvatarAppearanceDefines::ETextureIndex te, const LLColor4& new_color, BOOL upload_bake);
 	LLColor4		getClothesColor(LLAvatarAppearanceDefines::ETextureIndex te);
 	static BOOL		teToColorParams(LLAvatarAppearanceDefines::ETextureIndex te, U32 *param_name);
 
@@ -287,7 +289,7 @@ class LLAvatarAppearance : public LLCharacter
 	//--------------------------------------------------------------------
 public:
 	LLColor4		getGlobalColor(const std::string& color_name ) const;
-	virtual void	onGlobalColorChanged(const LLTexGlobalColor* global_color) = 0;
+	virtual void	onGlobalColorChanged(const LLTexGlobalColor* global_color, BOOL upload_bake) = 0;
 protected:
 	LLTexGlobalColor* mTexSkinColor;
 	LLTexGlobalColor* mTexHairColor;
diff --git a/indra/llappearance/lldriverparam.cpp b/indra/llappearance/lldriverparam.cpp
index 9e25c138d0f..e8263a3ac68 100644
--- a/indra/llappearance/lldriverparam.cpp
+++ b/indra/llappearance/lldriverparam.cpp
@@ -194,7 +194,7 @@ BOOL LLDriverParam::setInfo(LLDriverParamInfo *info)
 	mID = info->mID;
 	info->mDriverParam = this;
 
-	setWeight(getDefaultWeight());
+	setWeight(getDefaultWeight(), FALSE );
 
 	return TRUE;
 }
@@ -205,7 +205,7 @@ BOOL LLDriverParam::setInfo(LLDriverParamInfo *info)
 	return new LLDriverParam(*this);
 }
 
-void LLDriverParam::setWeight(F32 weight)
+void LLDriverParam::setWeight(F32 weight, BOOL upload_bake)
 {
 	F32 min_weight = getMinWeight();
 	F32 max_weight = getMaxWeight();
@@ -264,7 +264,7 @@ void LLDriverParam::setWeight(F32 weight)
 					driven_weight = driven_min;
 				}
 				
-				setDrivenWeight(&driven,driven_weight);
+				setDrivenWeight(&driven,driven_weight,upload_bake);
 				continue;
 			}
 			else 
@@ -288,13 +288,13 @@ void LLDriverParam::setWeight(F32 weight)
 					driven_weight = driven_min;
 				}
 
-				setDrivenWeight(&driven,driven_weight);
+				setDrivenWeight(&driven,driven_weight,upload_bake);
 				continue;
 			}
 		}
 
 		driven_weight = getDrivenWeight(&driven, mCurWeight);
-		setDrivenWeight(&driven,driven_weight);
+		setDrivenWeight(&driven,driven_weight,upload_bake);
 	}
 }
 
@@ -440,9 +440,9 @@ const LLViewerVisualParam* LLDriverParam::getDrivenParam(S32 index) const
 //-----------------------------------------------------------------------------
 // setAnimationTarget()
 //-----------------------------------------------------------------------------
-void LLDriverParam::setAnimationTarget( F32 target_value)
+void LLDriverParam::setAnimationTarget( F32 target_value, BOOL upload_bake )
 {
-	LLVisualParam::setAnimationTarget(target_value);
+	LLVisualParam::setAnimationTarget(target_value, upload_bake);
 
 	for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
 	{
@@ -451,16 +451,16 @@ void LLDriverParam::setAnimationTarget( F32 target_value)
 
 		// this isn't normally necessary, as driver params handle interpolation of their driven params
 		// but texture params need to know to assume their final value at beginning of interpolation
-		driven->mParam->setAnimationTarget(driven_weight);
+		driven->mParam->setAnimationTarget(driven_weight, upload_bake);
 	}
 }
 
 //-----------------------------------------------------------------------------
 // stopAnimating()
 //-----------------------------------------------------------------------------
-void LLDriverParam::stopAnimating()
+void LLDriverParam::stopAnimating(BOOL upload_bake)
 {
-	LLVisualParam::stopAnimating();
+	LLVisualParam::stopAnimating(upload_bake);
 
 	for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
 	{
@@ -540,7 +540,7 @@ void LLDriverParam::updateCrossDrivenParams(LLWearableType::EType driven_type)
 		LLWearable *wearable = mAvatarAppearance->getWearableData()->getTopWearable(driver_type);
 		if (wearable)
 		{
-			wearable->setVisualParamWeight(mID, wearable->getVisualParamWeight(mID));
+			wearable->setVisualParamWeight(mID, wearable->getVisualParamWeight(mID), false);
 		}
 	}
 }
@@ -603,7 +603,7 @@ F32 LLDriverParam::getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight
 	return driven_weight;
 }
 
-void LLDriverParam::setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight)
+void LLDriverParam::setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight, bool upload_bake)
 {
 	bool use_self = false;
 	if(mWearablep &&
@@ -620,10 +620,10 @@ void LLDriverParam::setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight)
 	if (use_self)
 	{
 		// call setWeight through LLVOAvatarSelf so other wearables can be updated with the correct values
-		mAvatarAppearance->setVisualParamWeight( (LLVisualParam*)driven->mParam, driven_weight);
+		mAvatarAppearance->setVisualParamWeight( (LLVisualParam*)driven->mParam, driven_weight, upload_bake);
 	}
 	else
 	{
-		driven->mParam->setWeight( driven_weight);
+		driven->mParam->setWeight( driven_weight, upload_bake);
 	}
 }
diff --git a/indra/llappearance/lldriverparam.h b/indra/llappearance/lldriverparam.h
index dcff02fc375..1af6541a79e 100644
--- a/indra/llappearance/lldriverparam.h
+++ b/indra/llappearance/lldriverparam.h
@@ -65,9 +65,9 @@ class LLDriverParamInfo final : public LLViewerVisualParamInfo
 	LLDriverParamInfo();
 	/*virtual*/ ~LLDriverParamInfo() = default;
 	
-	/*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+	/*virtual*/ BOOL parseXml(LLXmlTreeNode* node) override;
 
-	/*virtual*/ void toStream(std::ostream &out);	
+	/*virtual*/ void toStream(std::ostream &out) override;	
 
 protected:
 	typedef std::deque<LLDrivenEntryInfo> entry_info_list_t;
@@ -107,24 +107,24 @@ class LLDriverParam final : public LLViewerVisualParam
 
 	void					updateCrossDrivenParams(LLWearableType::EType driven_type);
 
-	/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const;
+	/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const override;
 
 	// LLVisualParam Virtual functions
-	/*virtual*/ void				apply( ESex sex ) {} // apply is called separately for each driven param.
-	/*virtual*/ void				setWeight(F32 weight);
-	/*virtual*/ void				setAnimationTarget( F32 target_value);
-	/*virtual*/ void				stopAnimating();
-	/*virtual*/ BOOL				linkDrivenParams(visual_param_mapper mapper, BOOL only_cross_params);
-	/*virtual*/ void				resetDrivenParams();
-	/*virtual*/ bool				isDriverParam() { return true; }
+	/*virtual*/ void				apply( ESex sex ) override {} // apply is called separately for each driven param.
+	/*virtual*/ void				setWeight(F32 weight, BOOL upload_bake) override;
+	/*virtual*/ void				setAnimationTarget( F32 target_value, BOOL upload_bake) override;
+	/*virtual*/ void				stopAnimating(BOOL upload_bake) override;
+	/*virtual*/ BOOL				linkDrivenParams(visual_param_mapper mapper, BOOL only_cross_params) override;
+	/*virtual*/ void				resetDrivenParams() override;
+	/*virtual*/ bool				isDriverParam() override { return true; }
 
 	// LLViewerVisualParam Virtual functions
-	/*virtual*/ F32					getTotalDistortion();
-	/*virtual*/ const LLVector4a&	getAvgDistortion();
-	/*virtual*/ F32					getMaxDistortion();
-	/*virtual*/ LLVector4a			getVertexDistortion(S32 index, LLPolyMesh *poly_mesh);
-	/*virtual*/ const LLVector4a*	getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh);
-	/*virtual*/ const LLVector4a*	getNextDistortion(U32 *index, LLPolyMesh **poly_mesh);
+	/*virtual*/ F32					getTotalDistortion() override;
+	/*virtual*/ const LLVector4a&	getAvgDistortion() override;
+	/*virtual*/ F32					getMaxDistortion() override;
+	/*virtual*/ LLVector4a			getVertexDistortion(S32 index, LLPolyMesh *poly_mesh) override;
+	/*virtual*/ const LLVector4a*	getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh) override;
+	/*virtual*/ const LLVector4a*	getNextDistortion(U32 *index, LLPolyMesh **poly_mesh) override;
 
 	S32								getDrivenParamsCount() const;
 	const LLViewerVisualParam*		getDrivenParam(S32 index) const;
@@ -136,7 +136,7 @@ class LLDriverParam final : public LLViewerVisualParam
 protected:
 	LLDriverParam(const LLDriverParam& pOther);
 	F32 getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight);
-	void setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight);
+	void setDrivenWeight(LLDrivenEntry *driven, F32 driven_weight, bool upload_bake);
 
 
 	LL_ALIGN_16(LLVector4a	mDefaultVec); // temp holder
diff --git a/indra/llappearance/llpolymorph.cpp b/indra/llappearance/llpolymorph.cpp
index c8837f3ae51..2d8ecce95fb 100644
--- a/indra/llappearance/llpolymorph.cpp
+++ b/indra/llappearance/llpolymorph.cpp
@@ -358,7 +358,7 @@ BOOL LLPolyMorphTarget::setInfo(LLPolyMorphTargetInfo* info)
 		return FALSE;
 	mInfo = info;
 	mID = info->mID;
-	setWeight(getDefaultWeight());
+	setWeight(getDefaultWeight(), FALSE);
 
 	LLAvatarAppearance* avatarp = mMesh->getAvatar();
 	LLPolyMorphTargetInfo::volume_info_list_t::iterator iter;
diff --git a/indra/llappearance/llpolyskeletaldistortion.cpp b/indra/llappearance/llpolyskeletaldistortion.cpp
index af8ea759a84..4b92af14572 100644
--- a/indra/llappearance/llpolyskeletaldistortion.cpp
+++ b/indra/llappearance/llpolyskeletaldistortion.cpp
@@ -121,7 +121,7 @@ BOOL LLPolySkeletalDistortion::setInfo(LLPolySkeletalDistortionInfo *info)
     }
     mInfo = info;
     mID = info->mID;
-    setWeight(getDefaultWeight());
+    setWeight(getDefaultWeight(), FALSE);
 
     LLPolySkeletalDistortionInfo::bone_info_list_t::iterator iter;
     for (iter = getInfo()->mBoneInfoList.begin(); iter != getInfo()->mBoneInfoList.end(); iter++)
diff --git a/indra/llappearance/lltexglobalcolor.cpp b/indra/llappearance/lltexglobalcolor.cpp
index 7ce5714f06f..20d7f3fd7f7 100644
--- a/indra/llappearance/lltexglobalcolor.cpp
+++ b/indra/llappearance/lltexglobalcolor.cpp
@@ -98,9 +98,9 @@ LLTexParamGlobalColor::LLTexParamGlobalColor(LLTexGlobalColor* tex_global_color)
 	return new LLTexParamGlobalColor(*this);
 }
 
-void LLTexParamGlobalColor::onGlobalColorChanged()
+void LLTexParamGlobalColor::onGlobalColorChanged(bool upload_bake)
 {
-	mAvatarAppearance->onGlobalColorChanged(mTexGlobalColor);
+	mAvatarAppearance->onGlobalColorChanged(mTexGlobalColor, upload_bake);
 }
 
 //-----------------------------------------------------------------------------
diff --git a/indra/llappearance/lltexglobalcolor.h b/indra/llappearance/lltexglobalcolor.h
index 718685899af..629ef22bf37 100644
--- a/indra/llappearance/lltexglobalcolor.h
+++ b/indra/llappearance/lltexglobalcolor.h
@@ -76,10 +76,10 @@ class LLTexParamGlobalColor final : public LLTexLayerParamColor
 public:
 	LLTexParamGlobalColor(LLTexGlobalColor *tex_color);
 	virtual ~LLTexParamGlobalColor() = default;
-	/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const;
+	/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable) const override;
 protected:
 	LLTexParamGlobalColor(const LLTexParamGlobalColor& pOther) = default;
-	/*virtual*/ void onGlobalColorChanged();
+	/*virtual*/ void onGlobalColorChanged(bool upload_bake) override;
 private:
 	LLTexGlobalColor*		mTexGlobalColor;
 };
diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp
index 918d13d4758..fa2e86396d0 100644
--- a/indra/llappearance/lltexlayer.cpp
+++ b/indra/llappearance/lltexlayer.cpp
@@ -158,7 +158,7 @@ BOOL LLTexLayerSetBuffer::renderTexLayerSet(LLRenderTarget* bound_target)
 									 getCompositeWidth(), getCompositeHeight(), bound_target );
 	gGL.flush();
 
-	midRenderTexLayerSet(success);
+	midRenderTexLayerSet(success, bound_target);
 
 	if (use_shaders)
 	{
@@ -181,8 +181,8 @@ BOOL LLTexLayerSetBuffer::renderTexLayerSet(LLRenderTarget* bound_target)
 
 LLTexLayerSetInfo::LLTexLayerSetInfo() :
 	mBodyRegion( "" ),
-	mWidth( 512 ),
-	mHeight( 512 ),
+	mWidth( 1024 ),
+	mHeight( 1024 ),
 	mClearAlpha( TRUE )
 {
 }
diff --git a/indra/llappearance/lltexlayer.h b/indra/llappearance/lltexlayer.h
index aaa32fd0296..a36e7754473 100644
--- a/indra/llappearance/lltexlayer.h
+++ b/indra/llappearance/lltexlayer.h
@@ -276,7 +276,7 @@ class LLTexLayerSetBuffer : public virtual LLRefCount
 	void					pushProjection() const;
 	void					popProjection() const;
 	virtual void			preRenderTexLayerSet();
-	virtual void			midRenderTexLayerSet(BOOL success) {}
+	virtual void			midRenderTexLayerSet(BOOL success, LLRenderTarget* bound_target) {}
 	virtual void			postRenderTexLayerSet(BOOL success);
 	virtual S32				getCompositeOriginX() const = 0;
 	virtual S32				getCompositeOriginY() const = 0;
diff --git a/indra/llappearance/lltexlayerparams.cpp b/indra/llappearance/lltexlayerparams.cpp
index a21696ad23a..79e3c165a1f 100644
--- a/indra/llappearance/lltexlayerparams.cpp
+++ b/indra/llappearance/lltexlayerparams.cpp
@@ -176,7 +176,7 @@ BOOL LLTexLayerParamAlpha::getMultiplyBlend() const
 	return ((LLTexLayerParamAlphaInfo *)getInfo())->mMultiplyBlend; 	
 }
 
-void LLTexLayerParamAlpha::setWeight(F32 weight)
+void LLTexLayerParamAlpha::setWeight(F32 weight, BOOL upload_bake)
 {
 	if (mIsAnimating || mTexLayer == NULL)
 	{
@@ -194,35 +194,35 @@ void LLTexLayerParamAlpha::setWeight(F32 weight)
 		if ((mAvatarAppearance->getSex() & getSex()) &&
 			(mAvatarAppearance->isSelf() && !mIsDummy)) // only trigger a baked texture update if we're changing a wearable's visual param.
 		{
-			mAvatarAppearance->invalidateComposite(mTexLayer->getTexLayerSet());
+			mAvatarAppearance->invalidateComposite(mTexLayer->getTexLayerSet(), upload_bake);
 			mTexLayer->invalidateMorphMasks();
 		}
 	}
 }
 
-void LLTexLayerParamAlpha::setAnimationTarget(F32 target_value)
+void LLTexLayerParamAlpha::setAnimationTarget(F32 target_value, BOOL upload_bake)
 { 
 	// do not animate dummy parameters
 	if (mIsDummy)
 	{
-		setWeight(target_value);
+		setWeight(target_value, upload_bake);
 		return;
 	}
 
 	mTargetWeight = target_value; 
-	setWeight(target_value); 
+	setWeight(target_value, upload_bake); 
 	mIsAnimating = TRUE;
 	if (mNext)
 	{
-		mNext->setAnimationTarget(target_value);
+		mNext->setAnimationTarget(target_value, upload_bake);
 	}
 }
 
-void LLTexLayerParamAlpha::animate(F32 delta)
+void LLTexLayerParamAlpha::animate(F32 delta, BOOL upload_bake)
 {
 	if (mNext)
 	{
-		mNext->animate(delta);
+		mNext->animate(delta, upload_bake);
 	}
 }
 
@@ -460,7 +460,7 @@ LLColor4 LLTexLayerParamColor::getNetColor() const
 }
 
 
-void LLTexLayerParamColor::setWeight(F32 weight)
+void LLTexLayerParamColor::setWeight(F32 weight, BOOL upload_bake)
 {
 	if (mIsAnimating)
 	{
@@ -486,10 +486,10 @@ void LLTexLayerParamColor::setWeight(F32 weight)
 
 		if ((mAvatarAppearance->getSex() & getSex()) && (mAvatarAppearance->isSelf() && !mIsDummy)) // only trigger a baked texture update if we're changing a wearable's visual param.
 		{
-			onGlobalColorChanged();
+			onGlobalColorChanged(upload_bake);
 			if (mTexLayer)
 			{
-				mAvatarAppearance->invalidateComposite(mTexLayer->getTexLayerSet());
+				mAvatarAppearance->invalidateComposite(mTexLayer->getTexLayerSet(), upload_bake);
 			}
 		}
 
@@ -497,23 +497,23 @@ void LLTexLayerParamColor::setWeight(F32 weight)
 	}
 }
 
-void LLTexLayerParamColor::setAnimationTarget(F32 target_value)
+void LLTexLayerParamColor::setAnimationTarget(F32 target_value, BOOL upload_bake)
 { 
 	// set value first then set interpolating flag to ignore further updates
 	mTargetWeight = target_value; 
-	setWeight(target_value);
+	setWeight(target_value, upload_bake);
 	mIsAnimating = TRUE;
 	if (mNext)
 	{
-		mNext->setAnimationTarget(target_value);
+		mNext->setAnimationTarget(target_value, upload_bake);
 	}
 }
 
-void LLTexLayerParamColor::animate(F32 delta)
+void LLTexLayerParamColor::animate(F32 delta, BOOL upload_bake)
 {
 	if (mNext)
 	{
-		mNext->animate(delta);
+		mNext->animate(delta, upload_bake);
 	}
 }
 
diff --git a/indra/llappearance/lltexlayerparams.h b/indra/llappearance/lltexlayerparams.h
index 1c593515019..0e66b1361c7 100644
--- a/indra/llappearance/lltexlayerparams.h
+++ b/indra/llappearance/lltexlayerparams.h
@@ -80,22 +80,22 @@ class LLTexLayerParamAlpha final : public LLTexLayerParam
 		ll_aligned_free_16(ptr);
 	}
 
-	/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable = NULL) const;
+	/*virtual*/ LLViewerVisualParam* cloneParam(LLWearable* wearable = nullptr) const override;
 
 	// LLVisualParam Virtual functions
 	///*virtual*/ BOOL		parseData(LLXmlTreeNode* node);
-	/*virtual*/ void		apply( ESex avatar_sex ) {}
-	/*virtual*/ void		setWeight(F32 weight);
-	/*virtual*/ void		setAnimationTarget(F32 target_value); 
-	/*virtual*/ void		animate(F32 delta);
+	/*virtual*/ void		apply( ESex avatar_sex ) override {}
+	/*virtual*/ void		setWeight(F32 weight, BOOL upload_bake) override;
+	/*virtual*/ void		setAnimationTarget(F32 target_value, BOOL upload_bake) override;
+	/*virtual*/ void		animate(F32 delta, BOOL upload_bake) override;
 
 	// LLViewerVisualParam Virtual functions
-	/*virtual*/ F32					getTotalDistortion()									{ return 1.f; }
-	/*virtual*/ const LLVector4a&	getAvgDistortion()										{ return mAvgDistortionVec; }
-	/*virtual*/ F32					getMaxDistortion()										{ return 3.f; }
-	/*virtual*/ LLVector4a			getVertexDistortion(S32 index, LLPolyMesh *poly_mesh)	{ return LLVector4a(1.f, 1.f, 1.f);}
-	/*virtual*/ const LLVector4a*	getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh)	{ index = 0; poly_mesh = NULL; return &mAvgDistortionVec;};
-	/*virtual*/ const LLVector4a*	getNextDistortion(U32 *index, LLPolyMesh **poly_mesh)	{ index = 0; poly_mesh = NULL; return NULL;};
+	/*virtual*/ F32					getTotalDistortion()								  override { return 1.f; }
+	/*virtual*/ const LLVector4a&	getAvgDistortion()									  override { return mAvgDistortionVec; }
+	/*virtual*/ F32					getMaxDistortion()									  override { return 3.f; }
+	/*virtual*/ LLVector4a			getVertexDistortion(S32 index, LLPolyMesh *poly_mesh) override { return LLVector4a(1.f, 1.f, 1.f);}
+	/*virtual*/ const LLVector4a*	getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh) override	{ index = 0; poly_mesh = NULL; return &mAvgDistortionVec;};
+	/*virtual*/ const LLVector4a*	getNextDistortion(U32 *index, LLPolyMesh **poly_mesh) override	{ index = 0; poly_mesh = NULL; return NULL;};
 
 	// New functions
 	BOOL					render( S32 x, S32 y, S32 width, S32 height );
@@ -129,7 +129,7 @@ class LLTexLayerParamAlphaInfo final : public LLViewerVisualParamInfo
 	LLTexLayerParamAlphaInfo();
 	/*virtual*/ ~LLTexLayerParamAlphaInfo() = default;
 
-	/*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+	/*virtual*/ BOOL parseXml(LLXmlTreeNode* node) override;
 
 private:
 	std::string				mStaticImageFileName;
@@ -177,26 +177,26 @@ class LLTexLayerParamColor : public LLTexLayerParam
 
 	// LLVisualParam Virtual functions
 	///*virtual*/ BOOL			parseData(LLXmlTreeNode* node);
-	/*virtual*/ void			apply( ESex avatar_sex ) {}
-	/*virtual*/ void			setWeight(F32 weight);
-	/*virtual*/ void			setAnimationTarget(F32 target_value);
-	/*virtual*/ void			animate(F32 delta);
+	/*virtual*/ void			apply( ESex avatar_sex ) override {}
+	/*virtual*/ void			setWeight(F32 weight, BOOL upload_bake) override;
+	/*virtual*/ void			setAnimationTarget(F32 target_value, BOOL upload_bake) override;
+	/*virtual*/ void			animate(F32 delta, BOOL upload_bake);
 
 
 	// LLViewerVisualParam Virtual functions
-	/*virtual*/ F32					getTotalDistortion()									{ return 1.f; }
-	/*virtual*/ const LLVector4a&	getAvgDistortion()										{ return mAvgDistortionVec; }
-	/*virtual*/ F32					getMaxDistortion()										{ return 3.f; }
-	/*virtual*/ LLVector4a			getVertexDistortion(S32 index, LLPolyMesh *poly_mesh)	{ return LLVector4a(1.f, 1.f, 1.f); }
-	/*virtual*/ const LLVector4a*	getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh)	{ index = 0; poly_mesh = NULL; return &mAvgDistortionVec;};
-	/*virtual*/ const LLVector4a*	getNextDistortion(U32 *index, LLPolyMesh **poly_mesh)	{ index = 0; poly_mesh = NULL; return NULL;};
+	/*virtual*/ F32					getTotalDistortion()									override { return 1.f; }
+	/*virtual*/ const LLVector4a&	getAvgDistortion()										override { return mAvgDistortionVec; }
+	/*virtual*/ F32					getMaxDistortion()										override { return 3.f; }
+	/*virtual*/ LLVector4a			getVertexDistortion(S32 index, LLPolyMesh *poly_mesh)	override { return LLVector4a(1.f, 1.f, 1.f); }
+	/*virtual*/ const LLVector4a*	getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh)	override { index = 0; poly_mesh = NULL; return &mAvgDistortionVec;};
+	/*virtual*/ const LLVector4a*	getNextDistortion(U32 *index, LLPolyMesh **poly_mesh)	override { index = 0; poly_mesh = NULL; return NULL;};
 
 	// New functions
 	LLColor4				getNetColor() const;
 protected:
 	LLTexLayerParamColor(const LLTexLayerParamColor& pOther) = default;
 
-	virtual void onGlobalColorChanged() {}
+	virtual void onGlobalColorChanged(bool upload_bake) {}
 private:
 	LL_ALIGN_16(LLVector4a				mAvgDistortionVec);
 } LL_ALIGN_POSTFIX(16);
@@ -208,7 +208,7 @@ class LLTexLayerParamColorInfo final : public LLViewerVisualParamInfo
 public:
 	LLTexLayerParamColorInfo();
 	virtual ~LLTexLayerParamColorInfo() = default;
-	BOOL parseXml( LLXmlTreeNode* node );
+	BOOL parseXml( LLXmlTreeNode* node ) override;
 	LLTexLayerParamColor::EColorOperation getOperation() const { return mOperation; }
 private:
 	enum { MAX_COLOR_VALUES = 20 };
diff --git a/indra/llappearance/llviewervisualparam.cpp b/indra/llappearance/llviewervisualparam.cpp
index 86d20b163c6..e083022d40d 100644
--- a/indra/llappearance/llviewervisualparam.cpp
+++ b/indra/llappearance/llviewervisualparam.cpp
@@ -134,7 +134,7 @@ BOOL LLViewerVisualParam::setInfo(LLViewerVisualParamInfo *info)
 		return FALSE;
 	mInfo = info;
 	mID = info->mID;
-	setWeight(getDefaultWeight());
+	setWeight(getDefaultWeight(), FALSE);
 	return TRUE;
 }
 
diff --git a/indra/llappearance/llwearable.cpp b/indra/llappearance/llwearable.cpp
index 9ea632639cf..8b687069afa 100644
--- a/indra/llappearance/llwearable.cpp
+++ b/indra/llappearance/llwearable.cpp
@@ -555,7 +555,7 @@ void LLWearable::revertValues()
 		LLVisualParam *param = getVisualParam(id);
 		if(param &&  !param->isDriverParam() )
 		{
-			setVisualParamWeight(id, value);
+			setVisualParamWeight(id, value, TRUE);
 		}
 	}
 
@@ -567,7 +567,7 @@ void LLWearable::revertValues()
 		LLVisualParam *param = getVisualParam(id);
 		if(param && param->isDriverParam())
 		{
-			setVisualParamWeight(id, value);
+			setVisualParamWeight(id, value, TRUE);
 		}
 	}
 
@@ -670,13 +670,13 @@ void LLWearable::addVisualParam(LLVisualParam *param)
 }
 
 
-void LLWearable::setVisualParamWeight(S32 param_index, F32 value)
+void LLWearable::setVisualParamWeight(S32 param_index, F32 value, BOOL upload_bake)
 {
 	auto iter = mVisualParamIndexMap.find(param_index);
 	if(iter != mVisualParamIndexMap.end())
 	{
 		LLVisualParam *wearable_param = iter->second;
-		wearable_param->setWeight(value);
+		wearable_param->setWeight(value, upload_bake);
 	}
 	else
 	{
@@ -718,12 +718,12 @@ void LLWearable::getVisualParams(visual_param_vec_t &list)
 	}
 }
 
-void LLWearable::animateParams(F32 delta)
+void LLWearable::animateParams(F32 delta, BOOL upload_bake)
 {
 	for(const auto& param_pair : mVisualParamIndexMap)
 	{
 		LLVisualParam *param = (LLVisualParam*)param_pair.second;
-		param->animate(delta);
+		param->animate(delta, upload_bake);
 	}
 }
 
@@ -741,14 +741,14 @@ LLColor4 LLWearable::getClothesColor(S32 te) const
 	return color;
 }
 
-void LLWearable::setClothesColor( S32 te, const LLColor4& new_color)
+void LLWearable::setClothesColor( S32 te, const LLColor4& new_color, BOOL upload_bake)
 {
 	U32 param_name[3];
 	if( LLAvatarAppearance::teToColorParams( (LLAvatarAppearanceDefines::ETextureIndex)te, param_name ) )
 	{
 		for( U8 index = 0; index < 3; index++ )
 		{
-			setVisualParamWeight(param_name[index], new_color.mV[index]);
+			setVisualParamWeight(param_name[index], new_color.mV[index], upload_bake);
 		}
 	}
 }
@@ -768,7 +768,7 @@ void LLWearable::writeToAvatar(LLAvatarAppearance* avatarp)
 			S32 param_id = param->getID();
 			F32 weight = getVisualParamWeight(param_id);
 
-			avatarp->setVisualParamWeight( param_id, weight);
+			avatarp->setVisualParamWeight( param_id, weight, FALSE);
 		}
 	}
 }
diff --git a/indra/llappearance/llwearable.h b/indra/llappearance/llwearable.h
index b72398b5ecb..aecb5088394 100644
--- a/indra/llappearance/llwearable.h
+++ b/indra/llappearance/llwearable.h
@@ -96,14 +96,14 @@ class LLWearable
 
 	void				setLocalTextureObject(S32 index, LLLocalTextureObject &lto);
 	void				addVisualParam(LLVisualParam *param);
-	void 				setVisualParamWeight(S32 index, F32 value);
+	void 				setVisualParamWeight(S32 index, F32 value, BOOL upload_bake);
 	F32					getVisualParamWeight(S32 index) const;
 	LLVisualParam*		getVisualParam(S32 index) const;
 	void				getVisualParams(visual_param_vec_t &list);
-	void				animateParams(F32 delta);
+	void				animateParams(F32 delta, BOOL upload_bake);
 
 	LLColor4			getClothesColor(S32 te) const;
-	void 				setClothesColor( S32 te, const LLColor4& new_color);
+	void 				setClothesColor( S32 te, const LLColor4& new_color, BOOL upload_bake);
 
 	virtual void		revertValues();
 	virtual void		saveValues();
diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp
index afc0c3f9f8f..d20b3421d84 100644
--- a/indra/llcharacter/llcharacter.cpp
+++ b/indra/llcharacter/llcharacter.cpp
@@ -295,13 +295,13 @@ void LLCharacter::removeAnimationData(std::string_view name)
 //-----------------------------------------------------------------------------
 // setVisualParamWeight()
 //-----------------------------------------------------------------------------
-BOOL LLCharacter::setVisualParamWeight(const LLVisualParam* which_param, F32 weight)
+BOOL LLCharacter::setVisualParamWeight(const LLVisualParam* which_param, F32 weight, BOOL upload_bake)
 {
 	S32 index = which_param->getID();
 	auto index_iter = mVisualParamIndexMap.find(index);
 	if (index_iter != mVisualParamIndexMap.end())
 	{
-		index_iter->second->setWeight(weight);
+		index_iter->second->setWeight(weight, upload_bake);
 		return TRUE;
 	}
 	return FALSE;
@@ -310,7 +310,7 @@ BOOL LLCharacter::setVisualParamWeight(const LLVisualParam* which_param, F32 wei
 //-----------------------------------------------------------------------------
 // setVisualParamWeight()
 //-----------------------------------------------------------------------------
-BOOL LLCharacter::setVisualParamWeight(const char* param_name, F32 weight)
+BOOL LLCharacter::setVisualParamWeight(const char* param_name, F32 weight, BOOL upload_bake)
 {
 	std::string tname(param_name);
 	LLStringUtil::toLower(tname);
@@ -318,7 +318,7 @@ BOOL LLCharacter::setVisualParamWeight(const char* param_name, F32 weight)
 	visual_param_name_map_t::iterator name_iter = mVisualParamNameMap.find(tableptr);
 	if (name_iter != mVisualParamNameMap.end())
 	{
-		name_iter->second->setWeight(weight);
+		name_iter->second->setWeight(weight, upload_bake);
 		return TRUE;
 	}
 	LL_WARNS() << "LLCharacter::setVisualParamWeight() Invalid visual parameter: " << param_name << LL_ENDL;
@@ -328,12 +328,12 @@ BOOL LLCharacter::setVisualParamWeight(const char* param_name, F32 weight)
 //-----------------------------------------------------------------------------
 // setVisualParamWeight()
 //-----------------------------------------------------------------------------
-BOOL LLCharacter::setVisualParamWeight(S32 index, F32 weight)
+BOOL LLCharacter::setVisualParamWeight(S32 index, F32 weight, BOOL upload_bake)
 {
 	auto index_iter = mVisualParamIndexMap.find(index);
 	if (index_iter != mVisualParamIndexMap.end())
 	{
-		index_iter->second->setWeight(weight);
+		index_iter->second->setWeight(weight, upload_bake);
 		return TRUE;
 	}
 	LL_WARNS() << "LLCharacter::setVisualParamWeight() Invalid visual parameter index: " << index << LL_ENDL;
@@ -402,7 +402,7 @@ void LLCharacter::clearVisualParamWeights()
 		LLVisualParam* param = param_pair.second;
 		if (param->isTweakable())
 		{
-			param->setWeight( param->getDefaultWeight());
+			param->setWeight( param->getDefaultWeight(), FALSE);
 		}
 	}
 }
diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h
index 1471dfb0306..ae43116d780 100644
--- a/indra/llcharacter/llcharacter.h
+++ b/indra/llcharacter/llcharacter.h
@@ -193,9 +193,10 @@ class LLCharacter
 	void addVisualParam(LLVisualParam *param);
 	void addSharedVisualParam(LLVisualParam *param);
 
-	virtual BOOL setVisualParamWeight(const LLVisualParam *which_param, F32 weight);
-	virtual BOOL setVisualParamWeight(const char* param_name, F32 weight);
-	virtual BOOL setVisualParamWeight(S32 index, F32 weight);
+	virtual BOOL setVisualParamWeight(const LLVisualParam *which_param, F32 weight, BOOL upload_bake = FALSE );
+	virtual BOOL setVisualParamWeight(const char* param_name, F32 weight, BOOL upload_bake = FALSE );
+	virtual BOOL setVisualParamWeight(S32 index, F32 weight, BOOL upload_bake = FALSE );
+
 
 	// get visual param weight by param or name
 	F32 getVisualParamWeight(LLVisualParam *distortion);
diff --git a/indra/llcharacter/llvisualparam.cpp b/indra/llcharacter/llvisualparam.cpp
index 34f0cf7bc12..f63cc632610 100644
--- a/indra/llcharacter/llvisualparam.cpp
+++ b/indra/llcharacter/llvisualparam.cpp
@@ -220,7 +220,7 @@ BOOL LLVisualParam::parseData(LLXmlTreeNode *node)
 //-----------------------------------------------------------------------------
 // setWeight()
 //-----------------------------------------------------------------------------
-void LLVisualParam::setWeight(F32 weight)
+void LLVisualParam::setWeight(F32 weight, BOOL upload_bake)
 {
 	if (mIsAnimating)
 	{
@@ -238,19 +238,19 @@ void LLVisualParam::setWeight(F32 weight)
 	
 	if (mNext)
 	{
-		mNext->setWeight(weight);
+		mNext->setWeight(weight, upload_bake);
 	}
 }
 
 //-----------------------------------------------------------------------------
 // setAnimationTarget()
 //-----------------------------------------------------------------------------
-void LLVisualParam::setAnimationTarget(F32 target_value)
+void LLVisualParam::setAnimationTarget(F32 target_value, BOOL upload_bake)
 {
 	// don't animate dummy parameters
 	if (mIsDummy)
 	{
-		setWeight(target_value);
+		setWeight(target_value, upload_bake);
 		mTargetWeight = mCurWeight;
 		return;
 	}
@@ -270,7 +270,7 @@ void LLVisualParam::setAnimationTarget(F32 target_value)
 
 	if (mNext)
 	{
-		mNext->setAnimationTarget(target_value);
+		mNext->setAnimationTarget(target_value, upload_bake);
 	}
 }
 
@@ -295,24 +295,24 @@ void LLVisualParam::clearNextParam()
 //-----------------------------------------------------------------------------
 // animate()
 //-----------------------------------------------------------------------------
-void LLVisualParam::animate( F32 delta)
+void LLVisualParam::animate( F32 delta, BOOL upload_bake)
 {
 	if (mIsAnimating)
 	{
 		F32 new_weight = ((mTargetWeight - mCurWeight) * delta) + mCurWeight;
-		setWeight(new_weight);
+		setWeight(new_weight, upload_bake);
 	}
 }
 
 //-----------------------------------------------------------------------------
 // stopAnimating()
 //-----------------------------------------------------------------------------
-void LLVisualParam::stopAnimating()
+void LLVisualParam::stopAnimating(BOOL upload_bake)
 { 
 	if (mIsAnimating && isTweakable())
 	{
 		mIsAnimating = FALSE; 
-		setWeight(mTargetWeight);
+		setWeight(mTargetWeight, upload_bake);
 	}
 }
 
diff --git a/indra/llcharacter/llvisualparam.h b/indra/llcharacter/llvisualparam.h
index 27c8ba2e6d9..41a6b808429 100644
--- a/indra/llcharacter/llvisualparam.h
+++ b/indra/llcharacter/llvisualparam.h
@@ -120,10 +120,10 @@ class LLVisualParam
 	//virtual BOOL			parseData( LLXmlTreeNode *node ) = 0;
 	virtual void			apply( ESex avatar_sex ) = 0;
 	//  Default functions
-	virtual void			setWeight(F32 weight);
-	virtual void			setAnimationTarget( F32 target_value);
-	virtual void			animate(F32 delta);
-	virtual void			stopAnimating();
+	virtual void			setWeight(F32 weight, BOOL upload_bake);
+	virtual void			setAnimationTarget( F32 target_value, BOOL upload_bake);
+	virtual void			animate(F32 delta, BOOL upload_bake);
+	virtual void			stopAnimating(BOOL upload_bake);
 
 	virtual BOOL			linkDrivenParams(visual_param_mapper mapper, BOOL only_cross_params);
 	virtual void			resetDrivenParams();
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index f6efc865efb..c64785e195e 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -140,6 +140,7 @@ set(viewer_SOURCE_FILES
     alunzip.cpp
     alviewermenu.cpp
     groupchatlistener.cpp
+    llagentwearablesfetch.cpp
     llaccountingcostmanager.cpp
     lladdgridhandler.cpp
     llaisapi.cpp
@@ -816,6 +817,7 @@ set(viewer_HEADER_FILES
     alviewermenu.h
     groupchatlistener.h
     llaccountingcost.h
+    llagentwearablesfetch.h
     llaccountingcostmanager.h
     lladdgridhandler.h
     llaisapi.h
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index da101a55db6..cbac637d057 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -472,6 +472,7 @@ LLAgent::LLAgent() :
     mCrouch(false),
 	mVoiceConnected(false),
 
+	mAppearanceSerialNum(0),
 	mMouselookModeInSignal(NULL),
 	mMouselookModeOutSignal(NULL)
 {
@@ -930,6 +931,28 @@ void LLAgent::standUp()
 // [/RLVa:KB]
 }
 
+void LLAgent::handleServerBakeRegionTransition(const LLUUID& region_id)
+{
+	LL_INFOS() << "called" << LL_ENDL;
+
+	// Old-style appearance entering a server-bake region.
+	if (isAgentAvatarValid() &&
+		!gAgentAvatarp->isUsingServerBakes() &&
+		(mRegionp->getCentralBakeVersion()>0))
+	{
+		LL_INFOS() << "update requested due to region transition" << LL_ENDL;
+		LLAppearanceMgr::instance().requestServerAppearanceUpdate();
+	}
+	// new-style appearance entering a non-bake region,
+	// need to check for existence of the baking service.
+	else if (isAgentAvatarValid() &&
+			 gAgentAvatarp->isUsingServerBakes() &&
+			 mRegionp->getCentralBakeVersion()==0)
+	{
+		gAgentAvatarp->checkForUnsupportedServerBakeAppearance();
+	}
+}
+
 void LLAgent::changeParcels()
 {
 	LL_DEBUGS("AgentLocation") << "Calling ParcelChanged callbacks" << LL_ENDL;
@@ -952,7 +975,6 @@ void LLAgent::capabilityReceivedCallback(const LLUUID &region_id, LLViewerRegion
     }
 }
 
-
 //-----------------------------------------------------------------------------
 // setRegion()
 //-----------------------------------------------------------------------------
@@ -1061,6 +1083,16 @@ void LLAgent::setRegion(LLViewerRegion *regionp)
 // [RLVa:KB] - Checked: 2011-05-27 (RLVa-1.4.0a) | Added: RLVa-1.4.0a
 	LLFloaterMove::sUpdateMovementStatus();
 // [/RLVa:KB]
+	// server.
+	if (mRegionp->capabilitiesReceived())
+	{
+		handleServerBakeRegionTransition(mRegionp->getRegionID());
+	}
+	else
+	{
+		// Need to handle via callback after caps arrive.
+		mRegionp->setCapabilitiesReceivedCallback(boost::bind(&LLAgent::handleServerBakeRegionTransition,this,_1));
+	}
 
 	LL_DEBUGS("AgentLocation") << "Calling RegionChanged callbacks" << LL_ENDL;
 	mRegionChangedSignal();
@@ -3946,6 +3978,82 @@ void LLAgent::processControlRelease(LLMessageSystem *msg, void **)
 }
 */
 
+//static
+void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data)
+{
+	gAgentQueryManager.mNumPendingQueries--;
+	if (gAgentQueryManager.mNumPendingQueries == 0)
+	{
+		selfStopPhase("fetch_texture_cache_entries");
+	}
+
+	if (!isAgentAvatarValid() || gAgentAvatarp->isDead())
+	{
+		LL_WARNS() << "No avatar for user in cached texture update!" << LL_ENDL;
+		return;
+	}
+
+	if (isAgentAvatarValid() && gAgentAvatarp->isEditingAppearance())
+	{
+		// ignore baked textures when in customize mode
+		return;
+	}
+
+	S32 query_id;
+	mesgsys->getS32Fast(_PREHASH_AgentData, _PREHASH_SerialNum, query_id);
+
+	S32 num_texture_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_WearableData);
+
+
+	S32 num_results = 0;
+	for (S32 texture_block = 0; texture_block < num_texture_blocks; texture_block++)
+	{
+		LLUUID texture_id;
+		U8 texture_index;
+
+		mesgsys->getUUIDFast(_PREHASH_WearableData, _PREHASH_TextureID, texture_id, texture_block);
+		mesgsys->getU8Fast(_PREHASH_WearableData, _PREHASH_TextureIndex, texture_index, texture_block);
+
+
+		if ((S32)texture_index < TEX_NUM_INDICES )
+		{
+			const LLAvatarAppearanceDictionary::TextureEntry *texture_entry = LLAvatarAppearance::getDictionary()->getTexture((ETextureIndex)texture_index);
+			if (texture_entry)
+			{
+				EBakedTextureIndex baked_index = texture_entry->mBakedTextureIndex;
+
+				if (gAgentQueryManager.mActiveCacheQueries[baked_index] == query_id)
+				{
+					if (texture_id.notNull())
+					{
+						//LL_INFOS() << "Received cached texture " << (U32)texture_index << ": " << texture_id << LL_ENDL;
+						gAgentAvatarp->setCachedBakedTexture((ETextureIndex)texture_index, texture_id);
+						//gAgentAvatarp->setTETexture( LLVOAvatar::sBakedTextureIndices[texture_index], texture_id );
+						gAgentQueryManager.mActiveCacheQueries[baked_index] = 0;
+						num_results++;
+					}
+					else
+					{
+						// no cache of this bake. request upload.
+						gAgentAvatarp->invalidateComposite(gAgentAvatarp->getLayerSet(baked_index),TRUE);
+					}
+				}
+			}
+		}
+	}
+	LL_INFOS() << "Received cached texture response for " << num_results << " textures." << LL_ENDL;
+	gAgentAvatarp->outputRezTiming("Fetched agent wearables textures from cache. Will now load them");
+
+	gAgentAvatarp->updateMeshTextures();
+
+	if (gAgentQueryManager.mNumPendingQueries == 0)
+	{
+		// RN: not sure why composites are disabled at this point
+		gAgentAvatarp->setCompositeUpdatesEnabled(TRUE);
+		gAgent.sendAgentSetAppearance();
+	}
+}
+
 BOOL LLAgent::anyControlGrabbed() const
 {
 	for (U32 i = 0; i < TOTAL_CONTROLS; i++)
@@ -4161,7 +4269,7 @@ bool LLAgent::hasPendingTeleportRequest()
 
 void LLAgent::startTeleportRequest()
 {
-    LL_INFOS("Telport") << "Agent handling start teleport request." << LL_ENDL;
+    LL_INFOS("Teleport") << "Agent handling start teleport request." << LL_ENDL;
     if(LLVoiceClient::instanceExists())
     {
         LLVoiceClient::getInstance()->setHidden(TRUE);
@@ -4829,6 +4937,211 @@ void LLAgent::requestLeaveGodMode()
 	sendReliableMessage();
 }
 
+// For debugging, trace agent state at times appearance message are sent out.
+void LLAgent::dumpSentAppearance(const std::string& dump_prefix)
+{
+	std::string outfilename = get_sequential_numbered_file_name(dump_prefix,".xml");
+
+	LLAPRFile outfile;
+	std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfilename);
+	outfile.open(fullpath, LL_APR_WB );
+	apr_file_t* file = outfile.getFileHandle();
+	if (!file)
+	{
+		return;
+	}
+	else
+	{
+		LL_DEBUGS("Avatar") << "dumping sent appearance message to " << fullpath << LL_ENDL;
+	}
+
+	LLVisualParam* appearance_version_param = gAgentAvatarp->getVisualParam(11000);
+	if (appearance_version_param)
+	{
+		F32 value = appearance_version_param->getWeight();
+		dump_visual_param(file, appearance_version_param, value);
+	}
+	for (const auto& iter : LLAvatarAppearance::getDictionary()->getTextures())
+    {
+		const ETextureIndex index = iter.first;
+		const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter.second;
+		if (texture_dict->mIsBakedTexture)
+		{
+			LLTextureEntry* entry = gAgentAvatarp->getTE((U8) index);
+			const LLUUID& uuid = entry->getID();
+			apr_file_printf( file, "\t\t<texture te=\"%i\" uuid=\"%s\"/>\n", index, uuid.asString().c_str());
+		}
+	}
+}
+
+//-----------------------------------------------------------------------------
+// sendAgentSetAppearance()
+//-----------------------------------------------------------------------------
+void LLAgent::sendAgentSetAppearance()
+{
+	if (gAgentQueryManager.mNumPendingQueries > 0) 
+	{
+		return;
+	}
+
+    LLViewerRegion* region = getRegion();
+	if (!isAgentAvatarValid() || gAgentAvatarp->isEditingAppearance()
+        || (region && region->getCentralBakeVersion()))
+    {
+        return;
+    }
+	// At this point we have a complete appearance to send and are in a non-baking region.
+	// DRANO FIXME
+	//gAgentAvatarp->setIsUsingServerBakes(FALSE);
+	S32 sb_count, host_count, both_count, neither_count;
+	gAgentAvatarp->bakedTextureOriginCounts(sb_count, host_count, both_count, neither_count);
+	if (both_count != 0 || neither_count != 0)
+	{
+		LL_WARNS() << "bad bake texture state " << sb_count << "," << host_count << "," << both_count << "," << neither_count << LL_ENDL;
+	}
+	if (sb_count != 0 && host_count == 0)
+	{
+		gAgentAvatarp->setIsUsingServerBakes(true);
+	}
+	else if (sb_count == 0 && host_count != 0)
+	{
+		gAgentAvatarp->setIsUsingServerBakes(false);
+	}
+	else if (sb_count + host_count > 0)
+	{
+		LL_WARNS() << "unclear baked texture state, not sending appearance" << LL_ENDL;
+		return;
+	}
+	
+
+	LL_INFOS("Avatar") << gAgentAvatarp->avString() << "TAT: Sent AgentSetAppearance: " << gAgentAvatarp->getBakedStatusForPrintout() << LL_ENDL;
+	//dumpAvatarTEs( "sendAgentSetAppearance()" );
+
+	LLMessageSystem* msg = gMessageSystem;
+	msg->newMessageFast(_PREHASH_AgentSetAppearance);
+	msg->nextBlockFast(_PREHASH_AgentData);
+	msg->addUUIDFast(_PREHASH_AgentID, getID());
+	msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+
+	// correct for the collision tolerance (to make it look like the 
+	// agent is actually walking on the ground/object)
+	// NOTE -- when we start correcting all of the other Havok geometry 
+	// to compensate for the COLLISION_TOLERANCE ugliness we will have 
+	// to tweak this number again
+    LLVector3 body_size = gAgentAvatarp->mBodySize;
+    if (region && region->simulatorFeaturesReceived() && region->avatarHoverHeightEnabled()) {
+        body_size += gAgentAvatarp->mAvatarOffset;
+    }
+	msg->addVector3Fast(_PREHASH_Size, body_size);	
+
+	// To guard against out of order packets
+	// Note: always start by sending 1.  This resets the server's count. 0 on the server means "uninitialized"
+	mAppearanceSerialNum++;
+	msg->addU32Fast(_PREHASH_SerialNum, mAppearanceSerialNum );
+
+	// is texture data current relative to wearables?
+	// KLW - TAT this will probably need to check the local queue.
+	BOOL textures_current = gAgentAvatarp->areTexturesCurrent();
+
+	if(textures_current)
+	{
+		for(U8 baked_index = 0; baked_index < gAgentAvatarp->getNumBakes(); baked_index++ )
+		{
+			const ETextureIndex texture_index = LLAvatarAppearance::getDictionary()->bakedToLocalTextureIndex((EBakedTextureIndex)baked_index);
+
+			// if we're not wearing a skirt, we don't need the texture to be baked
+			if (texture_index == TEX_SKIRT_BAKED && !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT))
+			{
+				continue;
+			}
+			// if we're not wearing a universal
+		    if ( (texture_index >= TEX_LEFT_ARM_BAKED && texture_index <= TEX_AUX3_BAKED) && !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_UNIVERSAL))
+        	{
+          		continue; // ignore universal that is also optional
+        	}
+			// IMG_DEFAULT_AVATAR means not baked. 0 index should be ignored for baked textures
+			if (!gAgentAvatarp->isTextureDefined(texture_index, 0))
+			{
+				LL_DEBUGS("Avatar") << "texture not current for baked " << (S32)baked_index << " local " << (S32)texture_index << LL_ENDL;
+				textures_current = FALSE;
+				break;
+			}
+		}
+	}
+
+	// only update cache entries if we have all our baked textures
+
+	// FIXME DRANO need additional check for not in appearance editing
+	// mode, if still using local composites need to set using local
+	// composites to false, and update mesh textures.
+	if (textures_current)
+	{
+		bool enable_verbose_dumps = gSavedSettings.getBOOL("DebugAvatarAppearanceMessage");
+		std::string dump_prefix = gAgentAvatarp->getFullname() + "_sent_appearance";
+		if (enable_verbose_dumps)
+		{
+			dumpSentAppearance(dump_prefix);
+		}
+		LL_INFOS("Avatar") << gAgentAvatarp->avString() << "TAT: Sending cached texture data" << LL_ENDL;
+		for (U8 baked_index = 0; baked_index < gAgentAvatarp->getNumBakes(); baked_index++)
+		{
+			BOOL generate_valid_hash = TRUE;
+			if (isAgentAvatarValid() && !gAgentAvatarp->isBakedTextureFinal((LLAvatarAppearanceDefines::EBakedTextureIndex)baked_index))
+			{
+				generate_valid_hash = FALSE;
+				LL_DEBUGS("Avatar") << gAgentAvatarp->avString() << "Not caching baked texture upload for " << (U32)baked_index << " due to being uploaded at low resolution." << LL_ENDL;
+			}
+
+			if (baked_index == BAKED_SKIRT && !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT))
+			{
+				LL_DEBUGS("Avatar") << "Not caching baked texture for unworn skirt." << LL_ENDL;
+				generate_valid_hash = FALSE;
+			}
+
+			const LLUUID hash = gAgentWearables.computeBakedTextureHash((EBakedTextureIndex) baked_index, generate_valid_hash);
+			if (hash.notNull())
+			{
+				ETextureIndex texture_index = LLAvatarAppearance::getDictionary()->bakedToLocalTextureIndex((EBakedTextureIndex) baked_index);
+				msg->nextBlockFast(_PREHASH_WearableData);
+				msg->addUUIDFast(_PREHASH_CacheID, hash);
+				msg->addU8Fast(_PREHASH_TextureIndex, (U8)texture_index);
+			}
+		}
+		msg->nextBlockFast(_PREHASH_ObjectData);
+		gAgentAvatarp->sendAppearanceMessage( gMessageSystem );
+	}
+	else
+	{
+		// If the textures aren't baked, send NULL for texture IDs
+		// This means the baked texture IDs on the server will be untouched.
+		// Once all textures are baked, another AvatarAppearance message will be sent to update the TEs
+		msg->nextBlockFast(_PREHASH_ObjectData);
+		gMessageSystem->addBinaryDataFast(_PREHASH_TextureEntry, nullptr, 0);
+	}
+
+
+	S32 transmitted_params = 0;
+	for (LLViewerVisualParam* param = (LLViewerVisualParam*)gAgentAvatarp->getFirstVisualParam();
+		 param;
+		 param = (LLViewerVisualParam*)gAgentAvatarp->getNextVisualParam())
+	{
+		if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ||
+				param->getGroup() == VISUAL_PARAM_GROUP_TRANSMIT_NOT_TWEAKABLE) // do not transmit params of group VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT
+		{
+			msg->nextBlockFast(_PREHASH_VisualParam );
+
+			// We don't send the param ids.  Instead, we assume that the receiver has the same params in the same sequence.
+			const F32 param_value = param->getWeight();
+			const U8 new_weight = F32_to_U8(param_value, param->getMinWeight(), param->getMaxWeight());
+			msg->addU8Fast(_PREHASH_ParamValue, new_weight );
+			transmitted_params++;
+		}
+	}
+
+	LL_INFOS() << "Avatar XML num VisualParams transmitted = " << transmitted_params << LL_ENDL;
+	sendReliableMessage();
+}
+
 void LLAgent::sendAgentDataUpdateRequest()
 {
 	gMessageSystem->newMessageFast(_PREHASH_AgentDataUpdateRequest);
@@ -5111,6 +5424,22 @@ void LLAgent::renderAutoPilotTarget()
 }
 
 /********************************************************************************/
+LLAgentQueryManager gAgentQueryManager;
+
+LLAgentQueryManager::LLAgentQueryManager() :
+	mWearablesCacheQueryID(0),
+	mNumPendingQueries(0),
+	mUpdateSerialNum(0)
+{
+	for (U32 i = 0; i < BAKED_NUM_INDICES; i++)
+	{
+		mActiveCacheQueries[i] = 0;
+	}
+}
+
+LLAgentQueryManager::~LLAgentQueryManager()
+{
+}
 
 //-----------------------------------------------------------------------------
 // LLTeleportRequest
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index b48acff65f9..aa5e4c2b0d6 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -717,6 +717,7 @@ class LLAgent final : public LLOldEvents::LLObservable
 
 	void            handleTeleportFinished();
 	void            handleTeleportFailed();
+	void			handleServerBakeRegionTransition(const LLUUID& region_id);
 
     static void     addTPNearbyChatSeparator();
     static void     onCapabilitiesReceivedAfterTeleport();
@@ -855,6 +856,7 @@ class LLAgent final : public LLOldEvents::LLObservable
 	
 private:
 	BOOL			mShowAvatar; 		// Should we render the avatar?
+	U32				mAppearanceSerialNum;
 
 	//--------------------------------------------------------------------
 	// Rendering state bitmap helpers
@@ -956,6 +958,8 @@ class LLAgent final : public LLOldEvents::LLObservable
 public:
 	void			sendMessage(); // Send message to this agent's region
 	void			sendReliableMessage();
+	void 			dumpSentAppearance(const std::string& dump_prefix);
+	void			sendAgentSetAppearance();
 	void 			sendAgentDataUpdateRequest();
 	void 			sendAgentUserInfoRequest();
 
@@ -977,6 +981,7 @@ class LLAgent final : public LLOldEvents::LLObservable
 	static void		processAgentGroupDataUpdate(LLMessageSystem *msg, void **);
 	static void		processAgentDropGroup(LLMessageSystem *msg, void **);
 	static void		processScriptControlChange(LLMessageSystem *msg, void **);
+	static void		processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data);
 	
 /**                    Messaging
  **                                                                            **
@@ -1023,4 +1028,24 @@ inline bool operator==(const LLGroupData &a, const LLGroupData &b)
 	return (a.mID == b.mID);
 }
 
+class LLAgentQueryManager
+{
+	friend class LLAgent;
+	friend class LLAgentWearables;
+	
+public:
+	LLAgentQueryManager();
+	virtual ~LLAgentQueryManager();
+	
+	BOOL 			hasNoPendingQueries() const 	{ return getNumPendingQueries() == 0; }
+	S32 			getNumPendingQueries() const 	{ return mNumPendingQueries; }
+private:
+	S32				mNumPendingQueries;
+	S32				mWearablesCacheQueryID;
+	U32				mUpdateSerialNum;
+	S32		    	mActiveCacheQueries[LLAvatarAppearanceDefines::BAKED_NUM_INDICES];
+};
+
+extern LLAgentQueryManager gAgentQueryManager;
+
 #endif
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 13ab1f878c2..89e2cd1dbce 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -56,6 +56,8 @@
 #include "rlvlocks.h"
 // [/RLVa:KB]
 
+#include "llagentwearablesfetch.h"
+#include "llviewernetwork.h"
 LLAgentWearables gAgentWearables;
 
 BOOL LLAgentWearables::mInitialWearablesUpdateReceived = FALSE;
@@ -227,6 +229,11 @@ void LLAgentWearables::initClass()
 void LLAgentWearables::setAvatarObject(LLVOAvatarSelf *avatar)
 {
 	llassert(avatar);
+	if (!LLGridManager::getInstance()->isInSecondlife())
+	{
+		avatar->outputRezTiming("Sending wearables request");
+		sendAgentWearablesRequest();
+	}
 	setAvatarAppearance(avatar);
 }
 
@@ -259,9 +266,24 @@ void LLAgentWearables::AddWearableToAgentInventoryCallback::fire(const LLUUID& i
 
 	gAgentWearables.addWearabletoAgentInventoryDone(mType, mIndex, inv_item, mWearable);
 
+	if (mTodo & CALL_UPDATE)
+	{
+		gAgentWearables.sendAgentWearablesUpdate();
+	}
+	if (mTodo & CALL_RECOVERDONE)
+	{
+		LLAppearanceMgr::instance().addCOFItemLink(inv_item);
+		gAgentWearables.recoverMissingWearableDone();
+	}
+
 	/*
 	 * Do this for every one in the loop
 	 */
+	if (mTodo & CALL_CREATESTANDARDDONE)
+	{
+		LLAppearanceMgr::instance().addCOFItemLink(inv_item);
+		gAgentWearables.createStandardWearablesDone(mType, mIndex);
+	}
 	if (mTodo & CALL_MAKENEWOUTFITDONE)
 	{
 		gAgentWearables.makeNewOutfitDone(mType, mIndex);
@@ -317,7 +339,81 @@ void LLAgentWearables::addWearabletoAgentInventoryDone(const LLWearableType::ETy
 	gInventory.notifyObservers();
 }
 
-void LLAgentWearables::saveWearable(const LLWearableType::EType type, const U32 index,
+void LLAgentWearables::sendAgentWearablesUpdate()
+{
+	// First make sure that we have inventory items for each wearable
+	for (S32 type=0; type < LLWearableType::WT_COUNT; ++type)
+	{
+		for (U32 index=0; index < getWearableCount((LLWearableType::EType)type); ++index)
+		{
+			LLViewerWearable* wearable = getViewerWearable((LLWearableType::EType)type,index);
+			if (wearable)
+			{
+				if (wearable->getItemID().isNull())
+				{
+					LLPointer<LLInventoryCallback> cb =
+						new AddWearableToAgentInventoryCallback(
+							LLPointer<LLRefCount>(NULL),
+							(LLWearableType::EType)type,
+							index,
+							wearable,
+							AddWearableToAgentInventoryCallback::CALL_NONE);
+					addWearableToAgentInventory(cb, wearable);
+				}
+				else
+				{
+					gInventory.addChangedMask(LLInventoryObserver::LABEL,
+											  wearable->getItemID());
+				}
+			}
+		}
+	}
+
+	// Then make sure the inventory is in sync with the avatar.
+	gInventory.notifyObservers();
+
+	// Send the AgentIsNowWearing 
+	gMessageSystem->newMessageFast(_PREHASH_AgentIsNowWearing);
+
+	gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+	gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+	gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+	LL_DEBUGS() << "sendAgentWearablesUpdate()" << LL_ENDL;
+	// MULTI-WEARABLE: DEPRECATED: HACK: index to 0- server database tables don't support concept of multiwearables.
+	for (S32 type=0; type < LLWearableType::WT_COUNT; ++type)
+	{
+		gMessageSystem->nextBlockFast(_PREHASH_WearableData);
+
+		U8 type_u8 = (U8)type;
+		gMessageSystem->addU8Fast(_PREHASH_WearableType, type_u8);
+
+		LLViewerWearable* wearable = getViewerWearable((LLWearableType::EType)type, 0);
+		if (wearable)
+		{
+			//LL_INFOS() << "Sending wearable " << wearable->getName() << LL_ENDL;
+			LLUUID item_id = wearable->getItemID();
+			const LLViewerInventoryItem *item = gInventory.getItem(item_id);
+			if (item && item->getIsLinkType())
+			{
+				// Get the itemID that this item points to.  i.e. make sure
+				// we are storing baseitems, not their links, in the database.
+				item_id = item->getLinkedUUID();
+			}
+			gMessageSystem->addUUIDFast(_PREHASH_ItemID, item_id);			
+		}
+		else
+		{
+			//LL_INFOS() << "Not wearing wearable type " << LLWearableType::getTypeName((LLWearableType::EType)i) << LL_ENDL;
+			gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID::null);
+		}
+
+		LL_DEBUGS() << "       " << LLWearableType::getInstance()->getTypeLabel((LLWearableType::EType)type) << ": " << (wearable ? wearable->getAssetID() : LLUUID::null) << LL_ENDL;
+	}
+	gAgent.sendReliableMessage();
+}
+
+void LLAgentWearables::saveWearable(const LLWearableType::EType type, const U32 index, BOOL send_update,
 									const std::string new_name)
 {
 	LLViewerWearable* old_wearable = getViewerWearable(type, index);
@@ -364,6 +460,11 @@ void LLAgentWearables::saveWearable(const LLWearableType::EType type, const U32
 		{
 			// Add a new inventory item (shouldn't ever happen here)
 			U32 todo = AddWearableToAgentInventoryCallback::CALL_NONE;
+			if (send_update)
+			{
+				todo |= AddWearableToAgentInventoryCallback::CALL_UPDATE;
+			}
+
 			LLPointer<LLInventoryCallback> cb =
 				new AddWearableToAgentInventoryCallback(
 					LLPointer<LLRefCount>(NULL),
@@ -375,7 +476,12 @@ void LLAgentWearables::saveWearable(const LLWearableType::EType type, const U32
 			return;
 		}
 
-		gAgentAvatarp->wearableUpdated(type);
+		gAgentAvatarp->wearableUpdated(type, TRUE);
+
+		if (send_update)
+		{
+			sendAgentWearablesUpdate();
+		}
 	}
 }
 
@@ -452,6 +558,8 @@ void LLAgentWearables::revertWearable(const LLWearableType::EType type, const U3
 	{
 		wearable->revertValues();
 	}
+
+	gAgent.sendAgentSetAppearance();
 }
 
 void LLAgentWearables::saveAllWearables()
@@ -464,8 +572,10 @@ void LLAgentWearables::saveAllWearables()
 	for (S32 i=0; i < LLWearableType::WT_COUNT; i++)
 	{
 		for (U32 j=0; j < getWearableCount((LLWearableType::EType)i); j++)
-			saveWearable((LLWearableType::EType)i, j);
+			saveWearable((LLWearableType::EType)i, j, FALSE);
 	}
+	sendAgentWearablesUpdate();
+	//gAgent.sendAgentSetAppearance();
 }
 
 // Called when the user changes the name of a wearable inventory item that is currently being worn.
@@ -494,6 +604,7 @@ void LLAgentWearables::setWearableName(const LLUUID& item_id, const std::string&
 				old_wearable->setName(old_name);
 
 				setWearable((LLWearableType::EType)i,j,new_wearable);
+				sendAgentWearablesUpdate();
 				break;
 			}
 		}
@@ -614,6 +725,15 @@ LLViewerWearable*	LLAgentWearables::getWearableFromAssetID(const LLUUID& asset_i
 	return NULL;
 }
 
+void LLAgentWearables::sendAgentWearablesRequest()
+{
+	gMessageSystem->newMessageFast(_PREHASH_AgentWearablesRequest);
+	gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+	gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+	gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+	gAgent.sendReliableMessage();
+}
+
 LLViewerWearable* LLAgentWearables::getViewerWearable(const LLWearableType::EType type, U32 index /*= 0*/)
 {
 	return dynamic_cast<LLViewerWearable*> (getWearable(type, index));
@@ -635,7 +755,8 @@ void LLAgentWearables::wearableUpdated(LLWearable *wearable, BOOL removed)
 {
 	if (isAgentAvatarValid())
 	{
-		gAgentAvatarp->wearableUpdated(wearable->getType());
+		const BOOL upload_result = removed;
+		gAgentAvatarp->wearableUpdated(wearable->getType(), upload_result);
 	}
 
 	LLWearableData::wearableUpdated(wearable, removed);
@@ -657,7 +778,7 @@ void LLAgentWearables::wearableUpdated(LLWearable *wearable, BOOL removed)
 			{
 				LL_INFOS() << "forcing wearable type " << wearable->getType() << " to version 22 from 24" << LL_ENDL;
 				wearable->setDefinitionVersion(22);
-				saveWearable(wearable->getType(),index);
+				saveWearable(wearable->getType(),index, TRUE);
 			}
 		}
 
@@ -665,6 +786,16 @@ void LLAgentWearables::wearableUpdated(LLWearable *wearable, BOOL removed)
 	}
 }
 
+BOOL LLAgentWearables::itemUpdatePending(const LLUUID& item_id) const
+{
+	return mItemsAwaitingWearableUpdate.find(item_id) != mItemsAwaitingWearableUpdate.end();
+}
+
+U32 LLAgentWearables::itemUpdatePendingCount() const
+{
+	return mItemsAwaitingWearableUpdate.size();
+}
+
 const LLUUID LLAgentWearables::getWearableItemID(LLWearableType::EType type, U32 index) const
 {
 	const LLViewerWearable *wearable = getViewerWearable(type,index);
@@ -716,6 +847,157 @@ BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id) const
 	return getWearableFromItemID(item_id) != NULL;
 }
 
+// MULTI-WEARABLE: DEPRECATED (see backwards compatibility)
+// static
+// ! BACKWARDS COMPATIBILITY ! When we stop supporting viewer1.23, we can assume
+// that viewers have a Current Outfit Folder and won't need this message, and thus
+// we can remove/ignore this whole function. EXCEPT gAgentWearables.notifyLoadingStarted
+void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgsys, void** user_data)
+{
+	// We should only receive this message a single time.  Ignore subsequent AgentWearablesUpdates
+	// that may result from AgentWearablesRequest having been sent more than once.
+	if (mInitialWearablesUpdateReceived)
+		return;
+
+	if (isAgentAvatarValid())
+	{
+		gAgentAvatarp->startPhase("process_initial_wearables_update");
+		gAgentAvatarp->outputRezTiming("Received initial wearables update");
+	}
+
+	// notify subscribers that wearables started loading. See EXT-7777
+	// *TODO: find more proper place to not be called from deprecated method.
+	// Seems such place is found: LLInitialWearablesFetch::processContents()
+	gAgentWearables.notifyLoadingStarted();
+
+	mInitialWearablesUpdateReceived = true;
+
+	LLUUID agent_id;
+	gMessageSystem->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+
+	if (isAgentAvatarValid() && (agent_id == gAgentAvatarp->getID()))
+	{
+		gMessageSystem->getU32Fast(_PREHASH_AgentData, _PREHASH_SerialNum, gAgentQueryManager.mUpdateSerialNum);
+
+		const S32 NUM_BODY_PARTS = 4;
+		S32 num_wearables = gMessageSystem->getNumberOfBlocksFast(_PREHASH_WearableData);
+		if (num_wearables < NUM_BODY_PARTS)
+		{
+			// Transitional state.  Avatars should always have at least their body parts (hair, eyes, shape and skin).
+			// The fact that they don't have any here (only a dummy is sent) implies that either:
+			// 1. This account existed before we had wearables
+			// 2. The database has gotten messed up
+			// 3. This is the account's first login (i.e. the wearables haven't been generated yet).
+			return;
+		}
+
+		// Get the UUID of the current outfit folder (will be created if it doesn't exist)
+		const LLUUID current_outfit_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
+		LLInitialWearablesFetch* outfit = new LLInitialWearablesFetch(current_outfit_id);
+
+		//LL_DEBUGS() << "processAgentInitialWearablesUpdate()" << LL_ENDL;
+		// Add wearables
+		// MULTI-WEARABLE: DEPRECATED: Message only supports one wearable per type, will be ignored in future.
+		gAgentWearables.mItemsAwaitingWearableUpdate.clear();
+		for (S32 i = 0; i < num_wearables; i++)
+		{
+			// Parse initial wearables data from message system
+			U8 type_u8 = 0;
+			gMessageSystem->getU8Fast(_PREHASH_WearableData, _PREHASH_WearableType, type_u8, i);
+			if (type_u8 >= LLWearableType::WT_COUNT)
+			{
+				continue;
+			}
+			const LLWearableType::EType type = (LLWearableType::EType) type_u8;
+
+			LLUUID item_id;
+			gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_ItemID, item_id, i);
+
+			LLUUID asset_id;
+			gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_AssetID, asset_id, i);
+			if (asset_id.isNull())
+			{
+				LLViewerWearable::removeFromAvatar(type, FALSE);
+			}
+			else
+			{
+				LLAssetType::EType asset_type = LLWearableType::getInstance()->getAssetType(type);
+				if (asset_type == LLAssetType::AT_NONE)
+				{
+					continue;
+				}
+
+				// MULTI-WEARABLE: DEPRECATED: this message only supports one wearable per type. Should be ignored in future versions
+
+				// Store initial wearables data until we know whether we have the current outfit folder or need to use the data.
+				LLInitialWearablesFetch::InitialWearableData wearable_data(type, item_id, asset_id);
+				outfit->add(wearable_data);
+			}
+
+			LL_DEBUGS() << "       " << LLWearableType::getInstance()->getTypeLabel(type) << LL_ENDL;
+		}
+
+		// Get the complete information on the items in the inventory and set up an observer
+		// that will trigger when the complete information is fetched.
+		outfit->startFetch();
+		if (outfit->isFinished())
+		{
+			// everything is already here - call done.
+			outfit->done();
+		}
+		else
+		{
+			// it's all on it's way - add an observer, and the inventory
+			// will call done for us when everything is here.
+			gInventory.addObserver(outfit);
+		}
+
+	}
+}
+
+// Normally, all wearables referred to "AgentWearablesUpdate" will correspond to actual assets in the
+// database.  If for some reason, we can't load one of those assets, we can try to reconstruct it so that
+// the user isn't left without a shape, for example.  (We can do that only after the inventory has loaded.)
+void LLAgentWearables::recoverMissingWearable(const LLWearableType::EType type, U32 index)
+{
+	// Try to recover by replacing missing wearable with a new one.
+	LLNotificationsUtil::add("ReplacedMissingWearable");
+	LL_DEBUGS() << "Wearable " << LLWearableType::getInstance()->getTypeLabel(type) << " could not be downloaded.  Replaced inventory item with default wearable." << LL_ENDL;
+	LLViewerWearable* new_wearable = LLWearableList::instance().createNewWearable(type, gAgentAvatarp);
+
+	setWearable(type,index,new_wearable);
+	//new_wearable->writeToAvatar(TRUE);
+
+	// Add a new one in the lost and found folder.
+	// (We used to overwrite the "not found" one, but that could potentially
+	// destory content.) JC
+	const LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
+	LLPointer<LLInventoryCallback> cb =
+		new AddWearableToAgentInventoryCallback(
+			LLPointer<LLRefCount>(NULL),
+			type,
+			index,
+			new_wearable,
+			AddWearableToAgentInventoryCallback::CALL_RECOVERDONE);
+	addWearableToAgentInventory(cb, new_wearable, lost_and_found_id, TRUE);
+}
+
+void LLAgentWearables::recoverMissingWearableDone()
+{
+	// Have all the wearables that the avatar was wearing at log-in arrived or been fabricated?
+	updateWearablesLoaded();
+	if (areWearablesLoaded())
+	{
+		// Make sure that the server's idea of the avatar's wearables actually match the wearables.
+		gAgent.sendAgentSetAppearance();
+	}
+	else
+	{
+		gInventory.addChangedMask(LLInventoryObserver::LABEL, LLUUID::null);
+		gInventory.notifyObservers();
+	}
+}
+
 void LLAgentWearables::addLocalTextureObject(const LLWearableType::EType wearable_type, const LLAvatarAppearanceDefines::ETextureIndex texture_type, U32 wearable_index)
 {
 	LLViewerWearable* wearable = getViewerWearable((LLWearableType::EType)wearable_type, wearable_index);
@@ -883,6 +1165,30 @@ void LLAgentWearables::sendDummyAgentWearablesUpdate()
 	gAgent.sendReliableMessage();
 }
 
+void LLAgentWearables::createStandardWearablesDone(S32 type, U32 index)
+{
+	LL_INFOS() << "type " << type << " index " << index << LL_ENDL;
+
+	if (!isAgentAvatarValid()) return;
+	gAgentAvatarp->updateVisualParams();
+}
+
+void LLAgentWearables::createStandardWearablesAllDone()
+{
+	// ... because sendAgentWearablesUpdate will notify inventory
+	// observers.
+	LL_INFOS() << "all done?" << LL_ENDL;
+
+	mWearablesLoaded = TRUE; 
+	checkWearablesLoaded();
+	notifyLoadingFinished();
+	
+	updateServer();
+
+	// Treat this as the first texture entry message, if none received yet
+	gAgentAvatarp->onFirstTEMessageReceived();
+}
+
 void LLAgentWearables::makeNewOutfitDone(S32 type, U32 index)
 {
 	LLUUID first_item_id = getWearableItemID((LLWearableType::EType)type, index);
@@ -917,6 +1223,13 @@ void LLAgentWearables::addWearableToAgentInventory(LLPointer<LLInventoryCallback
 
 void LLAgentWearables::removeWearable(const LLWearableType::EType type, bool do_remove_all, U32 index)
 {
+	if (gAgent.isTeen() &&
+		(type == LLWearableType::WT_UNDERSHIRT || type == LLWearableType::WT_UNDERPANTS))
+	{
+		// Can't take off underclothing in simple UI mode or on PG accounts
+		// TODO: enable the removing of a single undershirt/underpants if multiple are worn. - Nyx
+		return;
+	}
 	if (getWearableCount(type) == 0)
 	{
 		// no wearables to remove
@@ -991,7 +1304,7 @@ void LLAgentWearables::removeWearableFinal(const LLWearableType::EType type, boo
 			if (old_wearable)
 			{
 				eraseWearable(old_wearable);
-				old_wearable->removeFromAvatar();
+				old_wearable->removeFromAvatar(TRUE);
 			}
 		}
 //		clearWearableType(type);
@@ -1007,10 +1320,14 @@ void LLAgentWearables::removeWearableFinal(const LLWearableType::EType type, boo
 		if (old_wearable)
 		{
 			eraseWearable(old_wearable);
-			old_wearable->removeFromAvatar();
+			old_wearable->removeFromAvatar(TRUE);
 		}
 	}
 
+	queryWearableCache();
+
+	// Update the server
+	updateServer();
 	gInventory.notifyObservers();
 }
 
@@ -1044,6 +1361,7 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
 				continue;
 			}
 		}
+
 		if (type < 0 || type>=LLWearableType::WT_COUNT)
 		{
 			LL_WARNS() << "invalid type " << type << LL_ENDL;
@@ -1174,7 +1492,7 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
 
 	// Start rendering & update the server
 	mWearablesLoaded = TRUE; 
-
+	checkWearablesLoaded();
 // [SL:KB] - Patch: Appearance-InitialWearablesLoadedCallback | Checked: 2010-09-22 (Catznip-2.2)
 	if (!mInitialWearablesLoaded)
 	{
@@ -1190,6 +1508,9 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
 	// Then update the avatar based on the copied params.
 	gAgentAvatarp->updateVisualParams();
 
+	queryWearableCache();
+	updateServer();
+
 	gAgentAvatarp->dumpAvatarTEs("setWearableOutfit");
 
 	LL_DEBUGS("Avatar") << "setWearableOutfit() end" << LL_ENDL;
@@ -1249,6 +1570,12 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
 //		delete wearable;
 //		return false;
 //	}
+//	if (!new_item)
+//	{
+//		delete wearable;
+//		return false;
+//	}
+//
 //	switch(option)
 //	{
 //		case 0:  // "Save"
@@ -1309,8 +1636,81 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
 //		LL_INFOS() << "Replaced current element 0 for type " << type
 //				<< " size is now " << getWearableCount(type) << LL_ENDL;
 //	}
+//
+//	//LL_INFOS() << "LLVOAvatar::setWearableItem()" << LL_ENDL;
+//	queryWearableCache();
+//
+//	updateServer();
 //}
 
+void LLAgentWearables::queryWearableCache()
+{
+	if (!areWearablesLoaded() || (gAgent.getRegion() && gAgent.getRegion()->getCentralBakeVersion()))
+	{
+		return;
+	}
+	gAgentAvatarp->setIsUsingServerBakes(false);
+
+	// Look up affected baked textures.
+	// If they exist:
+	//		disallow updates for affected layersets (until dataserver responds with cache request.)
+	//		If cache miss, turn updates back on and invalidate composite.
+	//		If cache hit, modify baked texture entries.
+	//
+	// Cache requests contain list of hashes for each baked texture entry.
+	// Response is list of valid baked texture assets. (same message)
+
+	gMessageSystem->newMessageFast(_PREHASH_AgentCachedTexture);
+	gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+	gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+	gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+	gMessageSystem->addS32Fast(_PREHASH_SerialNum, gAgentQueryManager.mWearablesCacheQueryID);
+
+	S32 num_queries = 0;
+	for (U8 baked_index = 0; baked_index < gAgentAvatarp->getNumBakes(); baked_index++)
+	{
+		LLUUID hash_id = computeBakedTextureHash((EBakedTextureIndex) baked_index);
+		if (hash_id.notNull())
+		{
+			num_queries++;
+			// *NOTE: make sure at least one request gets packed
+
+			ETextureIndex te_index = LLAvatarAppearance::getDictionary()->bakedToLocalTextureIndex((EBakedTextureIndex)baked_index);
+
+			//LL_INFOS() << "Requesting texture for hash " << hash << " in baked texture slot " << baked_index << LL_ENDL;
+			gMessageSystem->nextBlockFast(_PREHASH_WearableData);
+			gMessageSystem->addUUIDFast(_PREHASH_ID, hash_id);
+			gMessageSystem->addU8Fast(_PREHASH_TextureIndex, (U8)te_index);
+		}
+
+		gAgentQueryManager.mActiveCacheQueries[baked_index] = gAgentQueryManager.mWearablesCacheQueryID;
+	}
+	//VWR-22113: gAgent.getRegion() can return null if invalid, seen here on logout
+	if(gAgent.getRegion())
+	{
+		if (isAgentAvatarValid())
+		{
+			selfStartPhase("fetch_texture_cache_entries");
+			gAgentAvatarp->outputRezTiming("Fetching textures from cache");
+		}
+
+		LL_DEBUGS("Avatar") << gAgentAvatarp->avString() << "Requesting texture cache entry for " << num_queries << " baked textures" << LL_ENDL;
+		gMessageSystem->sendReliable(gAgent.getRegion()->getHost());
+		gAgentQueryManager.mNumPendingQueries++;
+		gAgentQueryManager.mWearablesCacheQueryID++;
+	}
+}
+
+// virtual
+void LLAgentWearables::invalidateBakedTextureHash(LLMD5& hash) const
+{
+	// Add some garbage into the hash so that it becomes invalid.
+	if (isAgentAvatarValid())
+	{
+		hash.update((const unsigned char*)gAgentAvatarp->getID().mData, UUID_BYTES);
+	}
+}
+
 // User has picked "remove from avatar" from a menu.
 // static
 //void LLAgentWearables::userRemoveWearable(const LLWearableType::EType &type, const U32 &index)
@@ -1518,6 +1918,17 @@ void LLAgentWearables::userAttachMultipleAttachments(LLInventoryModel::item_arra
 // [/RLVa:KB]
 }
 
+void LLAgentWearables::checkWearablesLoaded() const
+{
+#ifdef SHOW_ASSERT
+	U32 item_pend_count = itemUpdatePendingCount();
+	if (mWearablesLoaded)
+	{
+		llassert(item_pend_count==0);
+	}
+#endif
+}
+
 // Returns false if the given wearable is already topmost/bottommost
 // (depending on closer_to_body parameter).
 bool LLAgentWearables::canMoveWearable(const LLUUID& item_id, bool closer_to_body) const
@@ -1534,9 +1945,20 @@ bool LLAgentWearables::canMoveWearable(const LLUUID& item_id, bool closer_to_bod
 
 BOOL LLAgentWearables::areWearablesLoaded() const
 {
+	checkWearablesLoaded();
 	return mWearablesLoaded;
 }
 
+// MULTI-WEARABLE: DEPRECATED: item pending count relies on old messages that don't support multi-wearables. do not trust to be accurate
+void LLAgentWearables::updateWearablesLoaded()
+{
+	mWearablesLoaded = (itemUpdatePendingCount()==0);
+	if (mWearablesLoaded)
+	{
+		notifyLoadingFinished();
+	}
+}
+
 bool LLAgentWearables::canWearableBeRemoved(const LLViewerWearable* wearable) const
 {
 	if (!wearable) return false;
@@ -1546,7 +1968,7 @@ bool LLAgentWearables::canWearableBeRemoved(const LLViewerWearable* wearable) co
 	return !(((type == LLWearableType::WT_SHAPE) || (type == LLWearableType::WT_SKIN) || (type == LLWearableType::WT_HAIR) || (type == LLWearableType::WT_EYES))
 			 && (getWearableCount(type) <= 1) );		  
 }
-void LLAgentWearables::animateAllWearableParams(F32 delta)
+void LLAgentWearables::animateAllWearableParams(F32 delta, BOOL upload_bake)
 {
 	for( S32 type = 0; type < LLWearableType::WT_COUNT; ++type )
 	{
@@ -1556,7 +1978,7 @@ void LLAgentWearables::animateAllWearableParams(F32 delta)
 			llassert(wearable);
 			if (wearable)
 			{
-				wearable->animateParams(delta);
+				wearable->animateParams(delta, upload_bake);
 			}
 		}
 	}
@@ -1698,6 +2120,12 @@ void LLAgentWearables::editWearableIfRequested(const LLUUID& item_id)
 	}
 }
 
+void LLAgentWearables::updateServer()
+{
+	sendAgentWearablesUpdate();
+	gAgent.sendAgentSetAppearance();
+}
+
 boost::signals2::connection LLAgentWearables::addLoadingStartedCallback(loading_started_callback_t cb)
 {
 	return mLoadingStartedSignal.connect(cb);
diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h
index 730f738405d..788589a1d06 100644
--- a/indra/newview/llagentwearables.h
+++ b/indra/newview/llagentwearables.h
@@ -43,6 +43,7 @@
 class LLInventoryItem;
 class LLVOAvatarSelf;
 class LLViewerWearable;
+class LLInitialWearablesFetch;
 class LLViewerObject;
 
 class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearableData
@@ -51,6 +52,7 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
 	// Constructors / destructors / Initializers
 	//--------------------------------------------------------------------
 public:
+	friend class LLInitialWearablesFetch;
 
 	LLAgentWearables();
 	virtual ~LLAgentWearables();
@@ -61,6 +63,9 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
 
 	// LLInitClass interface
 	static void initClass();
+protected:
+	void			createStandardWearablesDone(S32 type, U32 index/* = 0*/);
+	void			createStandardWearablesAllDone();
 	
 	//--------------------------------------------------------------------
 	// Queries
@@ -87,7 +92,7 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
 	// Note: False for shape, skin, eyes, and hair, unless you have MORE than 1.
 	bool			canWearableBeRemoved(const LLViewerWearable* wearable) const;
 
-	void			animateAllWearableParams(F32 delta);
+	void			animateAllWearableParams(F32 delta, BOOL upload_bake);
 
 	//--------------------------------------------------------------------
 	// Accessors
@@ -111,7 +116,7 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
 	// Setters
 	//--------------------------------------------------------------------
 private:
-	/*virtual*/void	wearableUpdated(LLWearable *wearable, BOOL removed);
+	/*virtual*/void	wearableUpdated(LLWearable *wearable, BOOL removed) override;
 public:
 //	void			setWearableItem(LLInventoryItem* new_item, LLViewerWearable* wearable, bool do_append = false);
 	void			setWearableOutfit(const LLInventoryItem::item_array_t& items, const std::vector< LLViewerWearable* >& wearables);
@@ -161,6 +166,22 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
 	void			removeWearableFinal(const LLWearableType::EType type, bool do_remove_all /*= false*/, U32 index /*= 0*/);
 protected:
 	static bool		onRemoveWearableDialog(const LLSD& notification, const LLSD& response);
+	
+	//--------------------------------------------------------------------
+	// Server Communication
+	//--------------------------------------------------------------------
+public:
+	// Processes the initial wearables update message (if necessary, since the outfit folder makes it redundant)
+	static void		processAgentInitialWearablesUpdate(LLMessageSystem* mesgsys, void** user_data);
+
+protected:
+
+	/*virtual*/ void	invalidateBakedTextureHash(LLMD5& hash) const override;
+	void			sendAgentWearablesUpdate();
+	void			sendAgentWearablesRequest();
+	void			queryWearableCache();
+	void 			updateServer();
+	static void		onInitialWearableAssetArrived(LLViewerWearable* wearable, void* userdata);
 
 	//--------------------------------------------------------------------
 	// Outfits
@@ -173,7 +194,7 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
 	//--------------------------------------------------------------------
 public:	
 	void			saveWearableAs(const LLWearableType::EType type, const U32 index, const std::string& new_name, const std::string& description, BOOL save_in_lost_and_found);
-	void			saveWearable(const LLWearableType::EType type, const U32 index,
+	void			saveWearable(const LLWearableType::EType type, const U32 index, BOOL send_update = TRUE,
 								 const std::string new_name = "");
 	void			saveAllWearables();
 	void			revertWearable(const LLWearableType::EType type, const U32 index);
@@ -199,6 +220,9 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
 	static void		userRemoveMultipleAttachments(llvo_vec_t& llvo_array);
 	static void		userAttachMultipleAttachments(LLInventoryModel::item_array_t& obj_item_array);
 
+	BOOL			itemUpdatePending(const LLUUID& item_id) const;
+	U32				itemUpdatePendingCount() const;
+
 	static llvo_vec_t getTempAttachments();
 
 	//--------------------------------------------------------------------
@@ -239,6 +263,7 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
 	static bool		mInitialAttachmentsRequested;
 // [/RLVa:KB]
 	BOOL			mWearablesLoaded;
+	std::set<LLUUID>	mItemsAwaitingWearableUpdate;
 
 	/**
 	 * True if agent's outfit is being changed now.
@@ -250,6 +275,18 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
 	// Support classes
 	//--------------------------------------------------------------------------------
 private:
+
+	class createStandardWearablesAllDoneCallback final : public LLRefCount
+	{
+	protected:
+		~createStandardWearablesAllDoneCallback();
+	};
+	class sendAgentWearablesUpdateCallback final : public LLRefCount
+	{
+	protected:
+		~sendAgentWearablesUpdateCallback();
+	};
+
 	class AddWearableToAgentInventoryCallback : public LLInventoryCallback
 	{
 	public:
@@ -269,7 +306,7 @@ class LLAgentWearables : public LLInitClass<LLAgentWearables>, public LLWearable
 											LLViewerWearable* wearable,
 											U32 todo = CALL_NONE,
 											const std::string description = "");
-		virtual void fire(const LLUUID& inv_item);
+		void fire(const LLUUID& inv_item) override;
 	private:
 		LLWearableType::EType mType;
 		U32 mIndex;
diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp
new file mode 100644
index 00000000000..a431aaaa452
--- /dev/null
+++ b/indra/newview/llagentwearablesfetch.cpp
@@ -0,0 +1,585 @@
+/** 
+ * @file llagentwearablesfetch.cpp
+ * @brief LLAgentWearblesFetch class implementation
+ *
+ * $LicenseInfo:firstyear=2001&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 "llagentwearablesfetch.h"
+
+#include "llagent.h"
+#include "llagentwearables.h"
+#include "llappearancemgr.h"
+#include "llinventoryfunctions.h"
+#include "llstartup.h"
+#include "llvoavatarself.h"
+
+
+void order_my_outfits_cb()
+{
+		if (!LLApp::isRunning())
+		{
+			LL_WARNS() << "called during shutdown, skipping" << LL_ENDL;
+			return;
+		}
+		
+		const LLUUID& my_outfits_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
+		if (my_outfits_id.isNull()) return;
+
+		LLInventoryModel::cat_array_t* cats;
+		LLInventoryModel::item_array_t* items;
+		gInventory.getDirectDescendentsOf(my_outfits_id, cats, items);
+		if (!cats) return;
+
+		//My Outfits should at least contain saved initial outfit and one another outfit
+		if (cats->size() < 2)
+		{
+			LL_WARNS() << "My Outfits category was not populated properly" << LL_ENDL;
+			return;
+		}
+
+		LL_INFOS() << "Starting updating My Outfits with wearables ordering information" << LL_ENDL;
+
+		for (LLInventoryModel::cat_array_t::iterator outfit_iter = cats->begin();
+			outfit_iter != cats->end(); ++outfit_iter)
+		{
+			const LLUUID& cat_id = (*outfit_iter)->getUUID();
+			if (cat_id.isNull()) continue;
+
+			// saved initial outfit already contains wearables ordering information
+			if (cat_id == LLAppearanceMgr::getInstance()->getBaseOutfitUUID()) continue;
+
+		LLAppearanceMgr::getInstance()->updateClothingOrderingInfo(cat_id);
+	}
+
+	LL_INFOS() << "Finished updating My Outfits with wearables ordering information" << LL_ENDL;
+}
+
+LLInitialWearablesFetch::LLInitialWearablesFetch(const LLUUID& cof_id) :
+	LLInventoryFetchDescendentsObserver(cof_id)
+{
+	if (isAgentAvatarValid())
+	{
+		gAgentAvatarp->startPhase("initial_wearables_fetch");
+		gAgentAvatarp->outputRezTiming("Initial wearables fetch started");
+	}
+}
+
+LLInitialWearablesFetch::~LLInitialWearablesFetch()
+{
+}
+
+// virtual
+void LLInitialWearablesFetch::done()
+{
+	// Delay processing the actual results of this so it's not handled within
+	// gInventory.notifyObservers.  The results will be handled in the next
+	// idle tick instead.
+	gInventory.removeObserver(this);
+	doOnIdleOneTime(boost::bind(&LLInitialWearablesFetch::processContents,this));
+	if (isAgentAvatarValid())
+	{
+		gAgentAvatarp->stopPhase("initial_wearables_fetch");
+		gAgentAvatarp->outputRezTiming("Initial wearables fetch done");
+	}
+}
+
+void LLInitialWearablesFetch::add(InitialWearableData &data)
+
+{
+	mAgentInitialWearables.push_back(data);
+}
+
+void LLInitialWearablesFetch::processContents()
+{
+	if(!gAgentAvatarp) //no need to process wearables if the agent avatar is deleted.
+	{
+		delete this;
+		return ;
+	}
+
+	// Fetch the wearable items from the Current Outfit Folder
+	LLInventoryModel::cat_array_t cat_array;
+	LLInventoryModel::item_array_t wearable_array;
+	LLFindWearables is_wearable;
+	llassert_always(!mComplete.empty());
+	gInventory.collectDescendentsIf(mComplete.front(), cat_array, wearable_array, 
+									LLInventoryModel::EXCLUDE_TRASH, is_wearable);
+
+	LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true);
+	if (!wearable_array.empty())
+	{
+		gAgentWearables.notifyLoadingStarted();
+		LLAppearanceMgr::instance().updateAppearanceFromCOF();
+	}
+	else
+	{
+		// if we're constructing the COF from the wearables message, we don't have a proper outfit link
+		LLAppearanceMgr::instance().setOutfitDirty(true);
+		processWearablesMessage();
+	}
+	delete this;
+}
+
+class LLFetchAndLinkObserver: public LLInventoryFetchItemsObserver
+{
+public:
+	LLFetchAndLinkObserver(uuid_vec_t& ids):
+		LLInventoryFetchItemsObserver(ids)
+	{
+	}
+	~LLFetchAndLinkObserver()
+	{
+	}
+
+	void done() override
+	{
+		gInventory.removeObserver(this);
+
+		// Link to all fetched items in COF.
+		LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy;
+		LLInventoryObject::const_object_list_t item_array;
+		for (auto& id : mIDs)
+        {
+			LLConstPointer<LLInventoryObject> item = gInventory.getItem(id);
+			if (!item)
+			{
+				LL_WARNS() << "fetch failed for item " << id << "!" << LL_ENDL;
+				continue;
+			}
+
+			item_array.push_back(item);
+		}
+		link_inventory_array(LLAppearanceMgr::instance().getCOF(), item_array, link_waiter);
+	}
+};
+
+void LLInitialWearablesFetch::processWearablesMessage()
+{
+	if (!mAgentInitialWearables.empty()) // We have an empty current outfit folder, use the message data instead.
+	{
+        (void) LLAppearanceMgr::instance().getCOF();
+		uuid_vec_t ids;
+		for (const auto& wearable_data : mAgentInitialWearables)
+        {
+			// Populate the current outfit folder with links to the wearables passed in the message
+            if (wearable_data.mAssetID.notNull())
+			{
+				ids.push_back(wearable_data.mItemID);
+			}
+			else
+			{
+				LL_INFOS() << "Invalid wearable, type " << wearable_data.mType << " itemID "
+				<< wearable_data.mItemID << " assetID " << wearable_data.mAssetID << LL_ENDL;
+			}
+		}
+
+		// Add all current attachments to the requested items as well.
+		if (isAgentAvatarValid())
+		{
+			for (LLVOAvatar::attachment_map_t::const_iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); 
+				 iter != gAgentAvatarp->mAttachmentPoints.end(); ++iter)
+			{
+				LLViewerJointAttachment* attachment = iter->second;
+				if (!attachment) continue;
+				for (LLViewerObject* attached_object : attachment->mAttachedObjects)
+                {
+                    if (!attached_object) continue;
+					const LLUUID& item_id = attached_object->getAttachmentItemID();
+					if (item_id.isNull()) continue;
+					ids.push_back(item_id);
+				}
+			}
+		}
+
+		// Need to fetch the inventory items for ids, then create links to them after they arrive.
+		LLFetchAndLinkObserver *fetcher = new LLFetchAndLinkObserver(ids);
+		fetcher->startFetch();
+		// If no items to be fetched, done will never be triggered.
+		// TODO: Change LLInventoryFetchItemsObserver::fetchItems to trigger done() on this condition.
+		if (fetcher->isFinished())
+		{
+			fetcher->done();
+		}
+		else
+		{
+			gInventory.addObserver(fetcher);
+		}
+	}
+	else
+	{
+		LL_WARNS("Wearables") << "No current outfit folder items found and no initial wearables fallback message received." << LL_ENDL;
+	}
+}
+
+LLLibraryOutfitsFetch::LLLibraryOutfitsFetch(const LLUUID& my_outfits_id) : 
+	LLInventoryFetchDescendentsObserver(my_outfits_id),
+	mCurrFetchStep(LOFS_FOLDER), 
+	mOutfitsPopulated(false) 
+{
+	LL_INFOS() << "created" << LL_ENDL;
+
+	mMyOutfitsID = LLUUID::null;
+	mClothingID = LLUUID::null;
+	mLibraryClothingID = LLUUID::null;
+	mImportedClothingID = LLUUID::null;
+	mImportedClothingName = "Imported Library Clothing";
+}
+
+LLLibraryOutfitsFetch::~LLLibraryOutfitsFetch()
+{
+	LL_INFOS() << "destroyed" << LL_ENDL;
+}
+
+void LLLibraryOutfitsFetch::done()
+{
+	LL_INFOS() << "start" << LL_ENDL;
+
+	// Delay this until idle() routine, since it's a heavy operation and
+	// we also can't have it run within notifyObservers.
+	doOnIdleOneTime(boost::bind(&LLLibraryOutfitsFetch::doneIdle,this));
+	gInventory.removeObserver(this); // Prevent doOnIdleOneTime from being added twice.
+}
+
+void LLLibraryOutfitsFetch::doneIdle()
+{
+	LL_INFOS() << "start" << LL_ENDL;
+
+	gInventory.addObserver(this); // Add this back in since it was taken out during ::done()
+	
+	switch (mCurrFetchStep)
+	{
+		case LOFS_FOLDER:
+			folderDone();
+			mCurrFetchStep = LOFS_OUTFITS;
+			break;
+		case LOFS_OUTFITS:
+			outfitsDone();
+			mCurrFetchStep = LOFS_LIBRARY;
+			break;
+		case LOFS_LIBRARY:
+			libraryDone();
+			mCurrFetchStep = LOFS_IMPORTED;
+			break;
+		case LOFS_IMPORTED:
+			importedFolderDone();
+			mCurrFetchStep = LOFS_CONTENTS;
+			break;
+		case LOFS_CONTENTS:
+			contentsDone();
+			break;
+		default:
+			LL_WARNS() << "Got invalid state for outfit fetch: " << mCurrFetchStep << LL_ENDL;
+			mOutfitsPopulated = TRUE;
+			break;
+	}
+
+	// We're completely done.  Cleanup.
+	if (mOutfitsPopulated)
+	{
+		gInventory.removeObserver(this);
+		delete this;
+		return;
+	}
+}
+
+void LLLibraryOutfitsFetch::folderDone()
+{
+	LL_INFOS() << "start" << LL_ENDL;
+
+	LLInventoryModel::cat_array_t cat_array;
+	LLInventoryModel::item_array_t wearable_array;
+	gInventory.collectDescendents(mMyOutfitsID, cat_array, wearable_array, 
+								  LLInventoryModel::EXCLUDE_TRASH);
+	
+	// Early out if we already have items in My Outfits
+	// except the case when My Outfits contains just initial outfit
+	if (cat_array.size() > 1)
+	{
+		mOutfitsPopulated = true;
+		return;
+	}
+
+	mClothingID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CLOTHING);
+	mLibraryClothingID = gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_CLOTHING, false);
+
+	// If Library->Clothing->Initial Outfits exists, use that.
+	LLNameCategoryCollector matchFolderFunctor("Initial Outfits");
+	cat_array.clear();
+	gInventory.collectDescendentsIf(mLibraryClothingID,
+									cat_array, wearable_array, 
+									LLInventoryModel::EXCLUDE_TRASH,
+									matchFolderFunctor);
+	if (cat_array.size() > 0)
+	{
+		const LLViewerInventoryCategory *cat = cat_array.at(0);
+		mLibraryClothingID = cat->getUUID();
+	}
+
+	mComplete.clear();
+	
+	// Get the complete information on the items in the inventory.
+	uuid_vec_t folders;
+	folders.push_back(mClothingID);
+	folders.push_back(mLibraryClothingID);
+	setFetchIDs(folders);
+	startFetch();
+	if (isFinished())
+	{
+		done();
+	}
+}
+
+void LLLibraryOutfitsFetch::outfitsDone()
+{
+	LL_INFOS() << "start" << LL_ENDL;
+
+	LLInventoryModel::cat_array_t cat_array;
+	LLInventoryModel::item_array_t wearable_array;
+	uuid_vec_t folders;
+	
+	// Collect the contents of the Library's Clothing folder
+	gInventory.collectDescendents(mLibraryClothingID, cat_array, wearable_array, 
+								  LLInventoryModel::EXCLUDE_TRASH);
+	
+	llassert(cat_array.size() > 0);
+	for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin();
+		 iter != cat_array.end();
+		 ++iter)
+	{
+		const LLViewerInventoryCategory *cat = iter->get();
+		
+		// Get the names and id's of every outfit in the library, skip "Ruth"
+		// because it's a low quality legacy outfit
+		if (cat->getName() != "Ruth")
+		{
+			// Get the name of every outfit in the library 
+			folders.push_back(cat->getUUID());
+			mLibraryClothingFolders.push_back(cat->getUUID());
+		}
+	}
+	cat_array.clear();
+	wearable_array.clear();
+
+	// Check if you already have an "Imported Library Clothing" folder
+	LLNameCategoryCollector matchFolderFunctor(mImportedClothingName);
+	gInventory.collectDescendentsIf(mClothingID, 
+									cat_array, wearable_array, 
+									LLInventoryModel::EXCLUDE_TRASH,
+									matchFolderFunctor);
+	if (cat_array.size() > 0)
+	{
+		const LLViewerInventoryCategory *cat = cat_array.at(0);
+		mImportedClothingID = cat->getUUID();
+	}
+	
+	mComplete.clear();
+	setFetchIDs(folders);
+	startFetch();
+	if (isFinished())
+	{
+		done();
+	}
+}
+
+class LLLibraryOutfitsCopyDone: public LLInventoryCallback
+{
+public:
+	LLLibraryOutfitsCopyDone(LLLibraryOutfitsFetch * fetcher):
+	mFireCount(0), mLibraryOutfitsFetcher(fetcher)
+	{
+	}
+	
+	virtual ~LLLibraryOutfitsCopyDone()
+	{
+		if (!LLApp::isExiting() && mLibraryOutfitsFetcher)
+		{
+			gInventory.addObserver(mLibraryOutfitsFetcher);
+			mLibraryOutfitsFetcher->done();
+		}
+	}
+	
+	/* virtual */ void fire(const LLUUID& inv_item)
+	{
+		mFireCount++;
+	}
+private:
+	U32 mFireCount;
+	LLLibraryOutfitsFetch * mLibraryOutfitsFetcher;
+};
+
+// Copy the clothing folders from the library into the imported clothing folder
+void LLLibraryOutfitsFetch::libraryDone()
+{
+	LL_INFOS() << "start" << LL_ENDL;
+
+	if (mImportedClothingID != LLUUID::null)
+	{
+		// Skip straight to fetching the contents of the imported folder
+		importedFolderFetch();
+		return;
+	}
+
+	// Remove observer; next autopopulation step will be triggered externally by LLLibraryOutfitsCopyDone.
+	gInventory.removeObserver(this);
+	
+	LLPointer<LLInventoryCallback> copy_waiter = new LLLibraryOutfitsCopyDone(this);
+	mImportedClothingID = gInventory.createNewCategory(mClothingID,
+													   LLFolderType::FT_NONE,
+													   mImportedClothingName);
+	// Copy each folder from library into clothing unless it already exists.
+	for (uuid_vec_t::const_iterator iter = mLibraryClothingFolders.begin();
+		 iter != mLibraryClothingFolders.end();
+		 ++iter)
+	{
+		const LLUUID& src_folder_id = (*iter); // Library clothing folder ID
+		const LLViewerInventoryCategory *cat = gInventory.getCategory(src_folder_id);
+		if (!cat)
+		{
+			LL_WARNS() << "Library folder import for uuid:" << src_folder_id << " failed to find folder." << LL_ENDL;
+			continue;
+		}
+		
+		if (!LLAppearanceMgr::getInstance()->getCanMakeFolderIntoOutfit(src_folder_id))
+		{
+			LL_INFOS() << "Skipping non-outfit folder name:" << cat->getName() << LL_ENDL;
+			continue;
+		}
+		
+		// Don't copy the category if it already exists.
+		LLNameCategoryCollector matchFolderFunctor(cat->getName());
+		LLInventoryModel::cat_array_t cat_array;
+		LLInventoryModel::item_array_t wearable_array;
+		gInventory.collectDescendentsIf(mImportedClothingID, 
+										cat_array, wearable_array, 
+										LLInventoryModel::EXCLUDE_TRASH,
+										matchFolderFunctor);
+		if (cat_array.size() > 0)
+		{
+			continue;
+		}
+
+		LLUUID dst_folder_id = gInventory.createNewCategory(mImportedClothingID,
+															LLFolderType::FT_NONE,
+															cat->getName());
+		LLAppearanceMgr::getInstance()->shallowCopyCategoryContents(src_folder_id, dst_folder_id, copy_waiter);
+	}
+}
+
+void LLLibraryOutfitsFetch::importedFolderFetch()
+{
+	LL_INFOS() << "start" << LL_ENDL;
+
+	// Fetch the contents of the Imported Clothing Folder
+	uuid_vec_t folders;
+	folders.push_back(mImportedClothingID);
+	
+	mComplete.clear();
+	setFetchIDs(folders);
+	startFetch();
+	if (isFinished())
+	{
+		done();
+	}
+}
+
+void LLLibraryOutfitsFetch::importedFolderDone()
+{
+	LL_INFOS() << "start" << LL_ENDL;
+
+	LLInventoryModel::cat_array_t cat_array;
+	LLInventoryModel::item_array_t wearable_array;
+	uuid_vec_t folders;
+	
+	// Collect the contents of the Imported Clothing folder
+	gInventory.collectDescendents(mImportedClothingID, cat_array, wearable_array, 
+								  LLInventoryModel::EXCLUDE_TRASH);
+	
+	for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array.begin();
+		 iter != cat_array.end();
+		 ++iter)
+	{
+		const LLViewerInventoryCategory *cat = iter->get();
+		
+		// Get the name of every imported outfit
+		folders.push_back(cat->getUUID());
+		mImportedClothingFolders.push_back(cat->getUUID());
+	}
+	
+	mComplete.clear();
+	setFetchIDs(folders);
+	startFetch();
+	if (isFinished())
+	{
+		done();
+	}
+}
+
+void LLLibraryOutfitsFetch::contentsDone()
+{		
+	LL_INFOS() << "start" << LL_ENDL;
+
+	LLInventoryModel::cat_array_t cat_array;
+	LLInventoryModel::item_array_t wearable_array;
+	
+	LLPointer<LLInventoryCallback> order_myoutfits_on_destroy = new LLBoostFuncInventoryCallback(no_op_inventory_func, order_my_outfits_cb);
+
+	for (uuid_vec_t::const_iterator folder_iter = mImportedClothingFolders.begin();
+		 folder_iter != mImportedClothingFolders.end();
+		 ++folder_iter)
+	{
+		const LLUUID &folder_id = (*folder_iter);
+		const LLViewerInventoryCategory *cat = gInventory.getCategory(folder_id);
+		if (!cat)
+		{
+			LL_WARNS() << "Library folder import for uuid:" << folder_id << " failed to find folder." << LL_ENDL;
+			continue;
+		}
+
+		//initial outfit should be already in My Outfits
+		if (cat->getName() == LLStartUp::getInitialOutfitName()) continue;
+		
+		// First, make a folder in the My Outfits directory.
+		LLUUID new_outfit_folder_id = gInventory.createNewCategory(mMyOutfitsID, LLFolderType::FT_OUTFIT, cat->getName());
+		
+		cat_array.clear();
+		wearable_array.clear();
+		// Collect the contents of each imported clothing folder, so we can create new outfit links for it
+		gInventory.collectDescendents(folder_id, cat_array, wearable_array, 
+									  LLInventoryModel::EXCLUDE_TRASH);
+		
+		LLInventoryObject::const_object_list_t item_array;
+		for (LLInventoryModel::item_array_t::const_iterator wearable_iter = wearable_array.begin();
+			 wearable_iter != wearable_array.end();
+			 ++wearable_iter)
+		{
+			LLConstPointer<LLInventoryObject> item = wearable_iter->get();
+			item_array.push_back(item);
+		}
+
+		link_inventory_array(new_outfit_folder_id, item_array, order_myoutfits_on_destroy);
+	}
+
+	mOutfitsPopulated = true;
+}
+
diff --git a/indra/newview/llagentwearablesfetch.h b/indra/newview/llagentwearablesfetch.h
new file mode 100644
index 00000000000..535db632251
--- /dev/null
+++ b/indra/newview/llagentwearablesfetch.h
@@ -0,0 +1,114 @@
+/** 
+ * @file llagentwearablesinitialfetch.h
+ * @brief LLAgentWearablesInitialFetch class header file
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLAGENTWEARABLESINITIALFETCH_H
+#define LL_LLAGENTWEARABLESINITIALFETCH_H
+
+#include "llinventoryobserver.h"
+#include "llwearabletype.h"
+#include "lluuid.h"
+
+//--------------------------------------------------------------------
+// InitialWearablesFetch
+// 
+// This grabs contents from the COF and processes them.
+// The processing is handled in idle(), i.e. outside of done(),
+// to avoid gInventory.notifyObservers recursion.
+//--------------------------------------------------------------------
+class LLInitialWearablesFetch : public LLInventoryFetchDescendentsObserver
+{
+	LOG_CLASS(LLInitialWearablesFetch);
+
+public:
+	LLInitialWearablesFetch(const LLUUID& cof_id);
+	~LLInitialWearablesFetch();
+	void done() override;
+
+	struct InitialWearableData
+	{
+		LLWearableType::EType mType;
+		LLUUID mItemID;
+		LLUUID mAssetID;
+		InitialWearableData(LLWearableType::EType type, LLUUID& itemID, LLUUID& assetID) :
+			mType(type),
+			mItemID(itemID),
+			mAssetID(assetID)
+		{}
+	};
+
+	void add(InitialWearableData &data);
+
+protected:
+	void processWearablesMessage();
+	void processContents();
+
+private:
+	typedef std::vector<InitialWearableData> initial_wearable_data_vec_t;
+	initial_wearable_data_vec_t mAgentInitialWearables; // Wearables from the old agent wearables msg
+};
+
+//--------------------------------------------------------------------
+// InitialWearablesFetch
+// 
+// This grabs outfits from the Library and copies those over to the user's
+// outfits folder, typically during first-ever login.
+//--------------------------------------------------------------------
+class LLLibraryOutfitsFetch : public LLInventoryFetchDescendentsObserver
+{
+public:
+	enum ELibraryOutfitFetchStep
+	{
+		LOFS_FOLDER = 0,
+		LOFS_OUTFITS,
+		LOFS_LIBRARY,
+		LOFS_IMPORTED,
+		LOFS_CONTENTS
+	};
+
+	LLLibraryOutfitsFetch(const LLUUID& my_outfits_id);
+	~LLLibraryOutfitsFetch();
+
+	virtual void done();
+	void doneIdle();
+	LLUUID mMyOutfitsID;
+	void importedFolderFetch();
+protected:
+	void folderDone();
+	void outfitsDone();
+	void libraryDone();
+	void importedFolderDone();
+	void contentsDone();
+	enum ELibraryOutfitFetchStep mCurrFetchStep;
+	uuid_vec_t mLibraryClothingFolders;
+	uuid_vec_t mImportedClothingFolders;
+	bool mOutfitsPopulated;
+	LLUUID mClothingID;
+	LLUUID mLibraryClothingID;
+	LLUUID mImportedClothingID;
+	std::string mImportedClothingName;
+};
+
+#endif // LL_AGENTWEARABLESINITIALFETCH_H
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 6f912b1860d..5e8ef78d797 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -4599,7 +4599,7 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b
 	bool result = false;
 	if ((result = gAgentWearables.moveWearable(item, closer_to_body)))
 	{
-		gAgentAvatarp->wearableUpdated(item->getWearableType());
+		gAgentAvatarp->wearableUpdated(item->getWearableType(), FALSE);
 	}
 
 	setOutfitDirty(true);
diff --git a/indra/newview/llemote.cpp b/indra/newview/llemote.cpp
index b9ef297c005..99e94602bf2 100644
--- a/indra/newview/llemote.cpp
+++ b/indra/newview/llemote.cpp
@@ -79,13 +79,13 @@ BOOL LLEmote::onActivate()
 	LLVisualParam* default_param = mCharacter->getVisualParam( "Express_Closed_Mouth" );
 	if( default_param )
 	{
-		default_param->setWeight( default_param->getMaxWeight());
+		default_param->setWeight( default_param->getMaxWeight(), FALSE);
 	}
 
 	mParam = mCharacter->getVisualParam(mName.c_str());
 	if (mParam)
 	{
-		mParam->setWeight(0.f);
+		mParam->setWeight(0.f, FALSE);
 		mCharacter->updateVisualParams();
 	}
 	
@@ -101,7 +101,7 @@ BOOL LLEmote::onUpdate(F32 time, U8* joint_mask)
 	if( mParam )
 	{
 		F32 weight = mParam->getMinWeight() + mPose.getWeight() * (mParam->getMaxWeight() - mParam->getMinWeight());
-		mParam->setWeight(weight);
+		mParam->setWeight(weight, FALSE);
 
 		// Cross fade against the default parameter
 		LLVisualParam* default_param = mCharacter->getVisualParam( "Express_Closed_Mouth" );
@@ -110,7 +110,7 @@ BOOL LLEmote::onUpdate(F32 time, U8* joint_mask)
 			F32 default_param_weight = default_param->getMinWeight() + 
 				(1.f - mPose.getWeight()) * ( default_param->getMaxWeight() - default_param->getMinWeight() );
 			
-			default_param->setWeight( default_param_weight);
+			default_param->setWeight( default_param_weight, FALSE );
 		}
 
 		mCharacter->updateVisualParams();
@@ -127,13 +127,13 @@ void LLEmote::onDeactivate()
 {
 	if( mParam )
 	{
-		mParam->setWeight( mParam->getDefaultWeight());
+		mParam->setWeight( mParam->getDefaultWeight(), FALSE);
 	}
 
 	LLVisualParam* default_param = mCharacter->getVisualParam( "Express_Closed_Mouth" );
 	if( default_param )
 	{
-		default_param->setWeight( default_param->getMaxWeight());
+		default_param->setWeight( default_param->getMaxWeight(), FALSE);
 	}
 
 	mCharacter->updateVisualParams();
diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp
index 07ffef8aa08..7a5f63b7ace 100644
--- a/indra/newview/lllocalbitmaps.cpp
+++ b/indra/newview/lllocalbitmaps.cpp
@@ -588,7 +588,7 @@ void LLLocalBitmap::updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableTyp
 						if (gAgentWearables.getWearableIndex(wearable,index))
 						{
 							gAgentAvatarp->setLocalTexture(reg_texind, gTextureList.getImage(new_id), FALSE, index);
-							gAgentAvatarp->wearableUpdated(type);
+							gAgentAvatarp->wearableUpdated(type, FALSE);
 							/* telling the manager to rebake once update cycle is fully done */
 							LLLocalBitmapMgr::getInstance()->setNeedsRebake();
 						}
diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp
index 16eaaafde67..cdec7825083 100644
--- a/indra/newview/llpaneleditwearable.cpp
+++ b/indra/newview/llpaneleditwearable.cpp
@@ -940,11 +940,11 @@ void LLPanelEditWearable::onCommitSexChange()
         LLViewerWearable*     wearable = gAgentWearables.getViewerWearable(type, index);
         if (wearable)
         {
-                wearable->setVisualParamWeight(param->getID(), is_new_sex_male);
+                wearable->setVisualParamWeight(param->getID(), is_new_sex_male, FALSE);
         }
-        param->setWeight( is_new_sex_male);
+        param->setWeight( is_new_sex_male, FALSE);
 
-        gAgentAvatarp->updateSexDependentLayerSets();
+        gAgentAvatarp->updateSexDependentLayerSets(FALSE);
 
         gAgentAvatarp->updateVisualParams();
         showWearable(mWearablePtr, TRUE, TRUE);
@@ -981,7 +981,7 @@ void LLPanelEditWearable::onTexturePickerCommit(const LLUICtrl* ctrl)
 							{
 								gAgentAvatarp->setLocalTexture(entry->mTextureIndex, image, FALSE, index);
 								LLVisualParamHint::requestHintUpdates();
-								gAgentAvatarp->wearableUpdated(type);
+								gAgentAvatarp->wearableUpdated(type, FALSE);
 							}
 							else
 							{
@@ -1010,9 +1010,9 @@ void LLPanelEditWearable::onColorSwatchCommit(const LLUICtrl* ctrl)
                         const LLColor4& new_color = LLColor4(ctrl->getValue());
                         if( old_color != new_color )
                         {
-                                getWearable()->setClothesColor(entry->mTextureIndex, new_color);
+                                getWearable()->setClothesColor(entry->mTextureIndex, new_color, TRUE);
                                 LLVisualParamHint::requestHintUpdates();
-                                gAgentAvatarp->wearableUpdated(getWearable()->getType());
+                                gAgentAvatarp->wearableUpdated(getWearable()->getType(), FALSE);
                         }
                 }
                 else
@@ -1113,7 +1113,7 @@ void LLPanelEditWearable::saveChanges(bool force_save_as)
 				// Remove old link
 				remove_inventory_item(link_item->getUUID(), gAgentAvatarp->mEndCustomizeCallback);
 			}
-			gAgentWearables.saveWearable(mWearablePtr->getType(), index, new_name);
+			gAgentWearables.saveWearable(mWearablePtr->getType(), index, TRUE, new_name);
         }
 
 	
@@ -1131,7 +1131,7 @@ void LLPanelEditWearable::revertChanges()
         mNameEditor->setText(mWearableItem->getName());
         updatePanelPickerControls(mWearablePtr->getType());
         updateTypeSpecificControls(mWearablePtr->getType());
-        gAgentAvatarp->wearableUpdated(mWearablePtr->getType());
+        gAgentAvatarp->wearableUpdated(mWearablePtr->getType(), FALSE);
 }
 
 void LLPanelEditWearable::showWearable(LLViewerWearable* wearable, BOOL show, BOOL disable_camera_switch)
@@ -1603,7 +1603,7 @@ void LLPanelEditWearable::onInvisibilityCommit(LLCheckBoxCtrl* checkbox_ctrl, LL
                 
                 LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture( IMG_INVISIBLE );
 				gAgentAvatarp->setLocalTexture(te, image, FALSE, index);
-				gAgentAvatarp->wearableUpdated(getWearable()->getType());
+				gAgentAvatarp->wearableUpdated(getWearable()->getType(), FALSE);
         }
         else
         {
@@ -1619,7 +1619,7 @@ void LLPanelEditWearable::onInvisibilityCommit(LLCheckBoxCtrl* checkbox_ctrl, LL
                 if (!image) return;
 
                 gAgentAvatarp->setLocalTexture(te, image, FALSE, index);
-                gAgentAvatarp->wearableUpdated(getWearable()->getType());
+                gAgentAvatarp->wearableUpdated(getWearable()->getType(), FALSE);
         }
 
         updatePanelPickerControls(getWearable()->getType());
diff --git a/indra/newview/llphysicsmotion.cpp b/indra/newview/llphysicsmotion.cpp
index c97a9b49e9e..a24f03b9c43 100644
--- a/indra/newview/llphysicsmotion.cpp
+++ b/indra/newview/llphysicsmotion.cpp
@@ -669,7 +669,7 @@ BOOL LLPhysicsMotion::onUpdate(F32 time)
 			if ((driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) &&
 			    (driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT))
 			{
-				mCharacter->setVisualParamWeight(driver_param, 0);
+				mCharacter->setVisualParamWeight(driver_param, 0, FALSE);
 			}
 			S32 num_driven = driver_param->getDrivenParamsCount();
 			for (S32 i = 0; i < num_driven; ++i)
@@ -769,5 +769,5 @@ void LLPhysicsMotion::setParamValue(const LLViewerVisualParam *param,
 	// Scale from [0,1] to [value_min_local,value_max_local]
         const F32 new_value_local = value_min_local + (value_max_local-value_min_local) * new_value_rescaled;
 
-        mCharacter->setVisualParamWeight(param, new_value_local);
+        mCharacter->setVisualParamWeight(param, new_value_local, FALSE);
 }
diff --git a/indra/newview/llscrollingpanelparam.cpp b/indra/newview/llscrollingpanelparam.cpp
index bfa453a0ae1..a7e24b86b13 100644
--- a/indra/newview/llscrollingpanelparam.cpp
+++ b/indra/newview/llscrollingpanelparam.cpp
@@ -266,7 +266,7 @@ void LLScrollingPanelParam::onHintHeldDown( LLVisualParamHint* hint )
 			if (slider->getMinValue() < new_percent
 				&& new_percent < slider->getMaxValue())
 			{
-				mWearable->setVisualParamWeight( hint->getVisualParam()->getID(), new_weight);
+				mWearable->setVisualParamWeight( hint->getVisualParam()->getID(), new_weight, FALSE);
 				mWearable->writeToAvatar(gAgentAvatarp);
 				gAgentAvatarp->updateVisualParams();
 
@@ -299,7 +299,7 @@ void LLScrollingPanelParam::onHintMinMouseUp( void* userdata )
 			if (slider->getMinValue() < new_percent
 				&& new_percent < slider->getMaxValue())
 			{
-				self->mWearable->setVisualParamWeight(hint->getVisualParam()->getID(), new_weight);
+				self->mWearable->setVisualParamWeight(hint->getVisualParam()->getID(), new_weight, FALSE);
 				self->mWearable->writeToAvatar(gAgentAvatarp);
 				slider->setValue( self->weightToPercent( new_weight ) );
 			}
@@ -333,7 +333,7 @@ void LLScrollingPanelParam::onHintMaxMouseUp( void* userdata )
 				if (slider->getMinValue() < new_percent
 					&& new_percent < slider->getMaxValue())
 				{
-					self->mWearable->setVisualParamWeight(hint->getVisualParam()->getID(), new_weight);
+					self->mWearable->setVisualParamWeight(hint->getVisualParam()->getID(), new_weight, FALSE);
 					self->mWearable->writeToAvatar(gAgentAvatarp);
 					slider->setValue( self->weightToPercent( new_weight ) );
 				}
diff --git a/indra/newview/llscrollingpanelparambase.cpp b/indra/newview/llscrollingpanelparambase.cpp
index fe7a3627235..b1ac39bfff7 100644
--- a/indra/newview/llscrollingpanelparambase.cpp
+++ b/indra/newview/llscrollingpanelparambase.cpp
@@ -93,7 +93,7 @@ void LLScrollingPanelParamBase::onSliderMoved(LLUICtrl* ctrl, void* userdata)
 	F32 new_weight = self->percentToWeight( (F32)slider->getValue().asReal() );
 	if (current_weight != new_weight )
 	{
-		self->mWearable->setVisualParamWeight( param->getID(), new_weight);
+		self->mWearable->setVisualParamWeight( param->getID(), new_weight, FALSE);
 		self->mWearable->writeToAvatar(gAgentAvatarp);
 		gAgentAvatarp->updateVisualParams();
 	}
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 04a202cd2e3..f32bbad72ab 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -2145,7 +2145,8 @@ bool idle_startup()
 		{
 			gAgentWearables.notifyLoadingStarted();
 			gAgent.setOutfitChosen(TRUE);
-			gAgentWearables.sendDummyAgentWearablesUpdate();
+			if (LLGridManager::getInstance()->isInSecondlife())
+				gAgentWearables.sendDummyAgentWearablesUpdate();
 			callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(), set_flags_and_update_appearance);
 		}
 
@@ -2206,22 +2207,22 @@ bool idle_startup()
 
 		if (gAgent.isOutfitChosen() && (wearables_time > max_wearables_time))
 		{
-			LLNotificationsUtil::add("ClothingLoading");
+			if (gInventory.isInventoryUsable())
+			{
+				LLNotificationsUtil::add("ClothingLoading");
+			}			
 			record(LLStatViewer::LOADING_WEARABLES_LONG_DELAY, wearables_time);
 			LLStartUp::setStartupState( STATE_CLEANUP );
 		}
+		// wait for avatar to be completely loaded
 		else if (gAgent.isFirstLogin()
 				&& isAgentAvatarValid()
 				&& gAgentAvatarp->isFullyLoaded())
 		{
-			// wait for avatar to be completely loaded
-			if (isAgentAvatarValid()
-				&& gAgentAvatarp->isFullyLoaded())
-			{
-				LL_DEBUGS("Avatar") << "avatar fully loaded" << LL_ENDL;
-				LLStartUp::setStartupState( STATE_CLEANUP );
-				return TRUE;
-			}
+			
+			LL_DEBUGS("Avatar") << "avatar fully loaded" << LL_ENDL;
+			LLStartUp::setStartupState( STATE_CLEANUP );
+			return TRUE;
 		}
 		else
 		{
@@ -2519,6 +2520,8 @@ void register_viewer_callbacks(LLMessageSystem* msg)
 	msg->setHandlerFuncFast(_PREHASH_AvatarAnimation,		process_avatar_animation);
 	msg->setHandlerFuncFast(_PREHASH_ObjectAnimation,		process_object_animation);
 	msg->setHandlerFuncFast(_PREHASH_AvatarAppearance,		process_avatar_appearance);
+	msg->setHandlerFunc("AgentCachedTextureResponse",	LLAgent::processAgentCachedTextureResponse);
+	msg->setHandlerFunc("RebakeAvatarTextures", LLVOAvatarSelf::processRebakeAvatarTextures);
 	msg->setHandlerFuncFast(_PREHASH_CameraConstraint,		process_camera_constraint);
 	msg->setHandlerFuncFast(_PREHASH_AvatarSitResponse,		process_avatar_sit_response);
 	msg->setHandlerFuncFast(_PREHASH_SetFollowCamProperties,			process_set_follow_cam_properties);
@@ -2592,6 +2595,8 @@ void register_viewer_callbacks(LLMessageSystem* msg)
 	// msg->setHandlerFuncFast(_PREHASH_ReputationIndividualReply,
 	//					LLFloaterRate::processReputationIndividualReply);
 
+	msg->setHandlerFuncFast(_PREHASH_AgentWearablesUpdate, LLAgentWearables::processAgentInitialWearablesUpdate );
+
 	msg->setHandlerFuncFast(_PREHASH_ScriptControlChange,
 						LLAgent::processScriptControlChange );
 
@@ -2772,7 +2777,10 @@ void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name,
 	}
 
 	gAgent.setOutfitChosen(TRUE);
-	gAgentWearables.sendDummyAgentWearablesUpdate();
+	if (LLGridManager::getInstance()->isInSecondlife())
+	{
+		gAgentWearables.sendDummyAgentWearablesUpdate();
+	}
 }
 
 std::string& LLStartUp::getInitialOutfitName()
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index 1351ec4d5c4..751eb1d26d4 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -435,6 +435,15 @@ void LLAvatarTexBar::draw()
 
 		LLColor4 text_color = LLColor4::white;
 
+		if (layerset_buffer->uploadNeeded())
+		{
+			text_color = LLColor4::red;
+		}
+		if (layerset_buffer->uploadInProgress())
+		{
+			text_color = LLColor4::magenta;
+		}
+
 		std::string text = layerset_buffer->dumpTextureInfo();
 		LLFontGL::getFontMonospace()->renderUTF8(text, 0, l_offset, v_offset + line_height*line_num,
 												 text_color, LLFontGL::LEFT, LLFontGL::TOP); //, LLFontGL::BOLD, LLFontGL::DROP_SHADOW_SOFT);
diff --git a/indra/newview/lltoolmorph.cpp b/indra/newview/lltoolmorph.cpp
index 8cdfe7feb82..6f3500c05ad 100644
--- a/indra/newview/lltoolmorph.cpp
+++ b/indra/newview/lltoolmorph.cpp
@@ -154,7 +154,7 @@ void LLVisualParamHint::preRender(BOOL clear_depth)
 		wearable->setVolatile(TRUE);
 	}
 	mLastParamWeight = mVisualParam->getWeight();
-	mWearablePtr->setVisualParamWeight(mVisualParam->getID(), mVisualParamWeight);
+	mWearablePtr->setVisualParamWeight(mVisualParam->getID(), mVisualParamWeight, FALSE);
 	gAgentAvatarp->setVisualParamWeight(mVisualParam->getID(), mVisualParamWeight);
 	gAgentAvatarp->setVisualParamWeight("Blink_Left", 0.f);
 	gAgentAvatarp->setVisualParamWeight("Blink_Right", 0.f);
@@ -253,8 +253,8 @@ BOOL LLVisualParamHint::render()
 		gGL.setSceneBlendType(LLRender::BT_ALPHA);
 		gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
 	}
-	gAgentAvatarp->setVisualParamWeight(mVisualParam->getID(), mLastParamWeight);
-	mWearablePtr->setVisualParamWeight(mVisualParam->getID(), mLastParamWeight);
+	gAgentAvatarp->setVisualParamWeight(mVisualParam->getID(), mLastParamWeight, FALSE);
+	mWearablePtr->setVisualParamWeight(mVisualParam->getID(), mLastParamWeight, FALSE);
 	LLViewerWearable* wearable = (LLViewerWearable*)mWearablePtr;
 	if (wearable)
 	{
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 0965d21dde0..9d15ddce714 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -3429,6 +3429,10 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)
 		LL_INFOS("Teleport") << "Agent movement complete, setting state to TELEPORT_START_ARRIVAL" << LL_ENDL;
 		gAgent.setTeleportState( LLAgent::TELEPORT_START_ARRIVAL );
 
+		// set the appearance on teleport since the new sim does not;
+		// know what you look like.
+		gAgent.sendAgentSetAppearance();
+
 		if (isAgentAvatarValid())
 		{
 			// Set the new position
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 1ae35a67a46..a33596cf641 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -174,8 +174,8 @@ LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pco
 			if (!gAgentAvatarp)
 			{
 				gAgentAvatarp = new LLVOAvatarSelf(id, pcode, regionp);
+				gAgentWearables.setAvatarObject(gAgentAvatarp); // Set before instance init
 				gAgentAvatarp->initInstance();
-				gAgentWearables.setAvatarObject(gAgentAvatarp);
 			}
 			else 
 			{
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 2d1baf690f9..ae5e57c3284 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -596,7 +596,7 @@ LLViewerRegion::LLViewerRegion(const U64 &handle,
 	mSimAccess( SIM_ACCESS_MIN ),
 	mBillableFactor(1.0),
 	mMaxTasks(DEFAULT_MAX_REGION_WIDE_PRIM_COUNT),
-	mCentralBakeVersion(1),
+	mCentralBakeVersion(0),
 	mClassID(0),
 	mCPURatio(0),
 	mColoName("unknown"),
diff --git a/indra/newview/llviewertexlayer.cpp b/indra/newview/llviewertexlayer.cpp
index 7b7c12a4c7a..ba403623d06 100644
--- a/indra/newview/llviewertexlayer.cpp
+++ b/indra/newview/llviewertexlayer.cpp
@@ -28,6 +28,9 @@
 
 #include "llviewertexlayer.h"
 
+#include "llfilesystem.h"
+#include "llsdutil.h"
+
 #include "llagent.h"
 #include "llimagej2c.h"
 #include "llnotificationsutil.h"
@@ -35,11 +38,32 @@
 #include "llglslshader.h"
 #include "llvoavatarself.h"
 #include "pipeline.h"
+#include "llviewerassetupload.h"
 #include "llviewercontrol.h"
+#include "llviewerstats.h"
+
+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();
 
+
+//-----------------------------------------------------------------------------
+// LLBakedUploadData()
+//-----------------------------------------------------------------------------
+LLBakedUploadData::LLBakedUploadData(const LLVOAvatarSelf* avatar,
+									 LLViewerTexLayerSet* layerset,
+									 const LLUUID& id,
+									 bool highest_res) :
+	mID(id),
+	mAvatar(avatar),
+	mTexLayerSet(layerset),
+	mStartTime(LLFrameTimer::getTotalTime()),		// Record starting time
+	mIsHighestRes(highest_res)
+{ 
+}
+
 //-----------------------------------------------------------------------------
 // LLViewerTexLayerSetBuffer
 // The composite image that a LLViewerTexLayerSet writes to.  Each LLViewerTexLayerSet has one.
@@ -53,12 +77,17 @@ LLViewerTexLayerSetBuffer::LLViewerTexLayerSetBuffer(LLTexLayerSet* const owner,
 	// ORDER_LAST => must render these after the hints are created.
 	LLTexLayerSetBuffer(owner),
     LLViewerDynamicTexture(width, height, 4, LLViewerDynamicTexture::ORDER_LAST, FALSE),
+	mNeedsUpload(FALSE), // Not used for any logic here, just to sync sending of updates
+	mNumLowresUploads(0),
+	mUploadPending(FALSE),
+	mUploadFailCount(0),
 	mNeedsUpdate(TRUE),
 	mNumLowresUpdates(0)
 {
 	mGLTexturep->setNeedsAlphaAndPickMask(FALSE);
 
 	LLViewerTexLayerSetBuffer::sGLByteCount += getSize();
+	mNeedsUploadTimer.start();
 	mNeedsUpdateTimer.start();
 }
 
@@ -101,6 +130,33 @@ void LLViewerTexLayerSetBuffer::requestUpdate()
 	restartUpdateTimer();
 	mNeedsUpdate = TRUE;
 	mNumLowresUpdates = 0;
+	// If we're in the middle of uploading a baked texture, we don't care about it any more.
+	// When it's downloaded, ignore it.
+	mUploadID.setNull();
+}
+
+void LLViewerTexLayerSetBuffer::requestUpload()
+{
+	conditionalRestartUploadTimer();
+	mNeedsUpload = TRUE;
+	mNumLowresUploads = 0;
+	mUploadPending = TRUE;
+}
+
+void LLViewerTexLayerSetBuffer::conditionalRestartUploadTimer()
+{
+	// If we requested a new upload but haven't even uploaded
+	// a low res version of our last upload request, then
+	// keep the timer ticking instead of resetting it.
+	if (mNeedsUpload && (mNumLowresUploads == 0))
+	{
+		mNeedsUploadTimer.unpause();
+	}
+	else
+	{
+		mNeedsUploadTimer.reset();
+		mNeedsUploadTimer.start();
+	}
 }
 
 void LLViewerTexLayerSetBuffer::restartUpdateTimer()
@@ -109,16 +165,25 @@ void LLViewerTexLayerSetBuffer::restartUpdateTimer()
 	mNeedsUpdateTimer.start();
 }
 
+void LLViewerTexLayerSetBuffer::cancelUpload()
+{
+	mNeedsUpload = FALSE;
+	mUploadPending = FALSE;
+	mNeedsUploadTimer.pause();
+	mUploadRetryTimer.reset();
+}
+
 // virtual
 BOOL LLViewerTexLayerSetBuffer::needsRender()
 {
 	llassert(mTexLayerSet->getAvatarAppearance() == gAgentAvatarp);
 	if (!isAgentAvatarValid()) return FALSE;
 
+	const BOOL upload_now = mNeedsUpload && isReadyToUpload();
 	const BOOL update_now = mNeedsUpdate && isReadyToUpdate();
 
 	// Don't render if we don't want to (or aren't ready to) update.
-	if (!update_now)
+	if (!(update_now || upload_now))
 	{
 		return FALSE;
 	}
@@ -133,6 +198,7 @@ BOOL LLViewerTexLayerSetBuffer::needsRender()
 	if (gAgentAvatarp->getBakedTE(getViewerTexLayerSet()) == LLAvatarAppearanceDefines::TEX_SKIRT_BAKED && 
 		!gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT))
 	{
+		cancelUpload();
 		return FALSE;
 	}
 
@@ -158,9 +224,46 @@ void LLViewerTexLayerSetBuffer::postRenderTexLayerSet(BOOL success)
 }
 
 // virtual
-void LLViewerTexLayerSetBuffer::midRenderTexLayerSet(BOOL success)
+void LLViewerTexLayerSetBuffer::midRenderTexLayerSet(BOOL success, LLRenderTarget* bound_target)
 {
+	// do we need to upload, and do we have sufficient data to create an uploadable composite?
+	// TODO: When do we upload the texture if gAgent.mNumPendingQueries is non-zero?
+	const BOOL upload_now = mNeedsUpload && isReadyToUpload();
 	const BOOL update_now = mNeedsUpdate && isReadyToUpdate();
+
+	if(upload_now)
+	{
+		if (!success)
+		{
+			LL_INFOS() << "Failed attempt to bake " << mTexLayerSet->getBodyRegionName() << LL_ENDL;
+			mUploadPending = FALSE;
+		}
+		else
+		{
+			LLViewerTexLayerSet* layer_set = getViewerTexLayerSet();
+			if (layer_set->isVisible())
+			{
+				auto bakedTexIdx = layer_set->getBakedTexIndex();
+				if(bakedTexIdx <= layer_set->getAvatar()->getNumBakes())
+				{
+					layer_set->getAvatar()->debugBakedTextureUpload(bakedTexIdx, FALSE); // FALSE for start of upload, TRUE for finish.
+					doUpload(bound_target);
+				}
+				else
+				{
+					LL_DEBUGS("Avatar") << "Skipping bake for unsupported layer on this region" << LL_ENDL;
+				}
+			}
+			else
+			{
+				mUploadPending = FALSE;
+				mNeedsUpload = FALSE;
+				mNeedsUploadTimer.pause();
+				layer_set->getAvatar()->setNewBakedTexture(layer_set->getBakedTexIndex(),IMG_INVISIBLE);
+			}
+		}
+	}
+
 	if (update_now)
 	{
 		doUpdate();
@@ -176,6 +279,60 @@ BOOL LLViewerTexLayerSetBuffer::isInitialized(void) const
 	return mGLTexturep.notNull() && mGLTexturep->isGLTextureCreated();
 }
 
+BOOL LLViewerTexLayerSetBuffer::uploadPending() const
+{
+	return mUploadPending;
+}
+
+BOOL LLViewerTexLayerSetBuffer::uploadNeeded() const
+{
+	return mNeedsUpload;
+}
+
+BOOL LLViewerTexLayerSetBuffer::uploadInProgress() const
+{
+	return !mUploadID.isNull();
+}
+
+BOOL LLViewerTexLayerSetBuffer::isReadyToUpload() const
+{
+	if (!gAgentQueryManager.hasNoPendingQueries()) return FALSE; // Can't upload if there are pending queries.
+	if (isAgentAvatarValid() && gAgentAvatarp->isEditingAppearance()) return FALSE; // Don't upload if avatar is being edited.
+
+	BOOL ready = FALSE;
+	if (getViewerTexLayerSet()->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);
+
+			// 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 = getViewerTexLayerSet()->isLocalTextureDataAvailable();
+			ready = has_lower_lod && is_upload_textures_timeout;
+		}
+	}
+
+	return ready;
+}
+
 BOOL LLViewerTexLayerSetBuffer::isReadyToUpdate() const
 {
 	// If we requested an update and have the final LOD ready, then update.
@@ -213,6 +370,232 @@ BOOL LLViewerTexLayerSetBuffer::requestUpdateImmediate()
 	return result;
 }
 
+//=========================================================================
+//-----------------------------------------------------------------------------
+// Support classes
+//-----------------------------------------------------------------------------
+class ALTexLayerUploader final : public LLBufferedAssetUploadInfo
+{
+public:
+	ALTexLayerUploader(LLUUID assetId, std::string texture, LLBakedUploadData* baked_upload_data);
+	~ALTexLayerUploader();
+
+    LLSD        prepareUpload() override;
+    LLSD        generatePostBody() override;
+    LLUUID      finishUpload(LLSD &result) override;
+
+private:
+	LLBakedUploadData* mBakedUploadData;
+};
+
+ALTexLayerUploader::ALTexLayerUploader(LLUUID assetId, std::string texture, LLBakedUploadData* baked_upload_data) :
+	LLBufferedAssetUploadInfo(LLUUID::null, LLAssetType::AT_TEXTURE, texture, NULL),
+	mBakedUploadData(baked_upload_data)
+{
+	setAssetId(assetId);
+}
+
+ALTexLayerUploader::~ALTexLayerUploader()
+{
+	delete_and_clear(mBakedUploadData);
+}
+
+LLSD ALTexLayerUploader::prepareUpload()
+{
+	return LLSD().with("success", LLSD::Boolean(true));
+}
+
+LLSD ALTexLayerUploader::generatePostBody()
+{   
+	return LLBufferedAssetUploadInfo::generatePostBody();
+}
+
+LLUUID ALTexLayerUploader::finishUpload(LLSD &result)
+{
+	LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]);
+
+	LLUUID new_id = LLUUID();
+
+	if (status.getType() == HTTP_OK)
+	{
+		new_id = result["new_asset"].asUUID();
+		std::string state = result["state"].asString();
+
+		LL_INFOS() << "result: " << state << " new_id: " << new_id << LL_ENDL;
+		if (state == "complete"
+			&& mBakedUploadData != NULL)
+		{	// Invoke 
+			LLViewerTexLayerSetBuffer::onTextureUploadComplete(new_id, static_cast<void*>(mBakedUploadData), 0, LLExtStat::NONE);
+			mBakedUploadData = NULL;	// deleted in onTextureUploadComplete()
+			return new_id;
+		}
+	}
+
+	LL_WARNS() << "Baked texture upload resulted in: " << status.getType() << ll_pretty_print_sd(result) << LL_ENDL;
+	// Invoke the original callback with an error result
+	LLViewerTexLayerSetBuffer::onTextureUploadComplete(new_id, static_cast<void*>(mBakedUploadData), -1, LLExtStat::NONE);
+	mBakedUploadData = NULL;	// deleted in onTextureUploadComplete()
+	return new_id;
+}
+
+// Create the baked texture, send it out to the server, then wait for it to come
+// back so we can switch to using it.
+void LLViewerTexLayerSetBuffer::doUpload(LLRenderTarget* bound_target)
+{
+	LLViewerTexLayerSet* layer_set = getViewerTexLayerSet();
+	LL_INFOS() << "Uploading baked " << layer_set->getBodyRegionName() << LL_ENDL;
+	add(LLStatViewer::TEX_BAKES, 1);
+
+	// Don't need caches since we're baked now.  (note: we won't *really* be baked 
+	// until this image is sent to the server and the Avatar Appearance message is received.)
+	layer_set->deleteCaches();
+
+	// Get the COLOR information from our texture
+	U8* baked_color_data = new U8[ mFullWidth * mFullHeight * 4 ];
+	glReadPixels(mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight, GL_RGBA, GL_UNSIGNED_BYTE, baked_color_data );
+	stop_glerror();
+
+	// Get the MASK information from our texture
+	LLGLSUIDefault gls_ui;
+	LLPointer<LLImageRaw> baked_mask_image = new LLImageRaw(mFullWidth, mFullHeight, 1 );
+	U8* baked_mask_data = baked_mask_image->getData(); 
+	layer_set->gatherMorphMaskAlpha(baked_mask_data,
+									mOrigin.mX, mOrigin.mY,
+									mFullWidth, mFullHeight, bound_target);
+
+
+	// Create the baked image from our color and mask information
+	const S32 baked_image_components = 5; // red green blue [bump] clothing
+	LLPointer<LLImageRaw> baked_image = new LLImageRaw( mFullWidth, mFullHeight, baked_image_components );
+	U8* baked_image_data = baked_image->getData();
+	S32 i = 0;
+	for (S32 u=0; u < mFullWidth; u++)
+	{
+		for (S32 v=0; v < mFullHeight; v++)
+		{
+			baked_image_data[5*i + 0] = baked_color_data[4*i + 0];
+			baked_image_data[5*i + 1] = baked_color_data[4*i + 1];
+			baked_image_data[5*i + 2] = baked_color_data[4*i + 2];
+			baked_image_data[5*i + 3] = baked_color_data[4*i + 3]; // alpha should be correct for eyelashes.
+			baked_image_data[5*i + 4] = baked_mask_data[i];
+			i++;
+		}
+	}
+	
+	LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C;
+	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))
+	{
+		LLTransactionID tid;
+		tid.generate();
+		const LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+		LLFileSystem up_file(asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE);
+		if (up_file.open() && up_file.write(compressedImage->getData(), compressedImage->getDataSize()))
+		{
+			up_file.close();
+			// Read back the file and validate.
+			BOOL valid = FALSE;
+			std::string asset_data;
+			{
+				LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
+				S32 file_size = 0;
+				LLFileSystem file(asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::READ);
+				file_size = file.getSize();
+				U8* data = integrity_test->allocateData(file_size);
+				if (data && file.open())
+				{
+					file.read(data, file_size);
+					file.close();
+					asset_data.append(reinterpret_cast<char const*> (data), file_size);
+					valid = integrity_test->validate(data, file_size); // integrity_test will delete 'data'
+				}
+				else
+				{
+					integrity_test->setLastError("Unable to read entire file");
+				}
+			}
+
+			if (valid)
+			{
+				const bool highest_lod = layer_set->isLocalTextureDataFinal();
+				// Baked_upload_data is owned by the responder and deleted after the request completes.
+				LLBakedUploadData* baked_upload_data = new LLBakedUploadData(gAgentAvatarp, 
+																			 layer_set, 
+																			 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.getRegionCapability("UploadBakedTexture");
+				if(!url.empty()
+					&& !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.
+				{
+					// The responder will call LLViewerTexLayerSetBuffer::onTextureUploadComplete()
+					LLResourceUploadInfo::ptr_t asset_info(new ALTexLayerUploader(mUploadID, asset_data, baked_upload_data));
+					LLViewerAssetUpload::EnqueueInventoryUpload(url, asset_info);
+					LL_INFOS() << "Baked texture upload via capability of " << mUploadID << " to " << url << LL_ENDL;
+				} 
+				else
+				{
+					gAssetStorage->storeAssetData(tid,
+												  LLAssetType::AT_TEXTURE,
+												  LLViewerTexLayerSetBuffer::onTextureUploadComplete,
+												  baked_upload_data,
+												  TRUE,		// temp_file
+												  TRUE,		// is_priority
+												  TRUE);	// store_local
+					LL_INFOS() << "Baked texture upload via Asset Store." <<  LL_ENDL;
+				}
+
+				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();
+				}
+				else
+				{
+					// Sending a lower level LOD for the baked texture.  Restart the upload timer.
+					mNumLowresUploads++;
+					mNeedsUploadTimer.unpause();
+					mNeedsUploadTimer.reset();
+				}
+
+				// Print out notification that we uploaded this texture.
+				if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
+				{
+					const std::string lod_str = highest_lod ? "HighRes" : "LowRes";
+					LLSD args;
+					args["EXISTENCE"] = llformat("%d",(U32)layer_set->getAvatar()->debugGetExistenceTimeElapsedF32());
+					args["TIME"] = llformat("%d",(U32)mNeedsUploadTimer.getElapsedTimeF32());
+					args["BODYREGION"] = layer_set->getBodyRegionName();
+					args["RESOLUTION"] = lod_str;
+					LLNotificationsUtil::add("AvatarRezSelfBakedTextureUploadNotification",args);
+					LL_DEBUGS("Avatar") << self_av_string() << "Uploading [ name: " << layer_set->getBodyRegionName() << " res:" << lod_str << " time:" << (U32)mNeedsUploadTimer.getElapsedTimeF32() << " ]" << LL_ENDL;
+				}
+			}
+			else
+			{
+				// The read back and validate operation failed.  Remove the uploaded file.
+				mUploadPending = FALSE;
+				up_file.remove();
+				LL_INFOS() << "Unable to create baked upload file (reason: corrupted)." << LL_ENDL;
+			}
+		}
+	}
+	else
+	{
+		// The VFS write file operation failed.
+		mUploadPending = FALSE;
+		LL_INFOS() << "Unable to create baked upload file (reason: failed to write file)" << LL_ENDL;
+	}
+
+	delete [] baked_color_data;
+}
+
 // Mostly bookkeeping; don't need to actually "do" anything since
 // render() will actually do the update.
 void LLViewerTexLayerSetBuffer::doUpdate()
@@ -249,6 +632,82 @@ void LLViewerTexLayerSetBuffer::doUpdate()
 	}
 }
 
+// static
+void LLViewerTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid,
+												  void* userdata,
+												  S32 result,
+												  LLExtStat ext_status) // StoreAssetData callback (not fixed)
+{
+	LLBakedUploadData* baked_upload_data = (LLBakedUploadData*)userdata;
+
+	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()))
+	{
+		LLViewerTexLayerSetBuffer* layerset_buffer = baked_upload_data->mTexLayerSet->getViewerComposite();
+		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
+
+			// BAP: does this really belong in this callback, as
+			// opposed to where the cancellation takes place?
+			// suspect this does nothing.
+			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; // Allows sending of AgentSetAppearance later
+				LLAvatarAppearanceDefines::ETextureIndex baked_te = gAgentAvatarp->getBakedTE(layerset_buffer->getViewerTexLayerSet());
+				// Update baked texture info with the new UUID
+				U64 now = LLFrameTimer::getTotalTime();		// Record starting time
+				LL_INFOS() << "Baked" << resolution << "texture upload for " << name << " took " << (S32)((now - baked_upload_data->mStartTime) / 1000) << " ms" << LL_ENDL;
+				gAgentAvatarp->setNewBakedTexture(baked_te, uuid);
+			}
+			else
+			{	
+				++failures;
+				S32 max_attempts = baked_upload_data->mIsHighestRes ? BAKE_UPLOAD_ATTEMPTS : 1; // only retry final bakes
+				LL_WARNS() << "Baked" << resolution << "texture upload for " << name << " failed (attempt " << failures << "/" << max_attempts << ")" << LL_ENDL;
+				if (failures < max_attempts)
+				{
+					layerset_buffer->mUploadFailCount = failures;
+					layerset_buffer->mUploadRetryTimer.start();
+					layerset_buffer->requestUpload();
+				}
+			}
+		}
+		else
+		{
+			LL_INFOS() << "Received baked texture out of date, ignored." << LL_ENDL;
+		}
+
+		gAgentAvatarp->dirtyMesh();
+	}
+	else
+	{
+		// Baked texture failed to upload (in which case since we
+		// didn't set the new baked texture, it means that they'll try
+		// and rebake it at some point in the future (after login?)),
+		// or this response to upload is out of date, in which case a
+		// current response should be on the way or already processed.
+		LL_WARNS() << "Baked upload failed" << LL_ENDL;
+	}
+
+	delete baked_upload_data;
+}
+
 //-----------------------------------------------------------------------------
 // LLViewerTexLayerSet
 // An ordered set of texture layers that get composited into a single texture.
@@ -290,6 +749,20 @@ void LLViewerTexLayerSet::requestUpdate()
 	}
 }
 
+void LLViewerTexLayerSet::requestUpload()
+{
+	createComposite();
+	getViewerComposite()->requestUpload();
+}
+
+void LLViewerTexLayerSet::cancelUpload()
+{
+	if(mComposite)
+	{
+		getViewerComposite()->cancelUpload();
+	}
+}
+
 void LLViewerTexLayerSet::updateComposite()
 {
 	createComposite();
@@ -342,12 +815,19 @@ const std::string LLViewerTexLayerSetBuffer::dumpTextureInfo() const
 {
 	if (!isAgentAvatarValid()) return "";
 
-	const BOOL is_high_res = TRUE; 
-	const U32 num_low_res = 0;
+	const BOOL is_high_res = !mNeedsUpload;
+	const U32 num_low_res = mNumLowresUploads;
+	const U32 upload_time = (U32)mNeedsUploadTimer.getElapsedTimeF32();
 	const std::string local_texture_info = gAgentAvatarp->debugDumpLocalTextureDataInfo(getViewerTexLayerSet());
 
-	std::string text = llformat("[HiRes:%d LoRes:%d] %s",
+	std::string status 				= "CREATING ";
+	if (!uploadNeeded()) status 	= "DONE     ";
+	if (uploadInProgress()) status 	= "UPLOADING";
+
+	std::string text = llformat("[%s] [HiRes:%d LoRes:%d] [Elapsed:%d] %s",
+								status.c_str(),
 								is_high_res, num_low_res,
+								upload_time, 
 								local_texture_info.c_str());
 	return text;
 }
diff --git a/indra/newview/llviewertexlayer.h b/indra/newview/llviewertexlayer.h
index 3a83423df44..e549eb90f68 100644
--- a/indra/newview/llviewertexlayer.h
+++ b/indra/newview/llviewertexlayer.h
@@ -46,11 +46,13 @@ class LLViewerTexLayerSet final : public LLTexLayerSet
 	LLViewerTexLayerSet(LLAvatarAppearance* const appearance);
 	virtual ~LLViewerTexLayerSet();
 
-	/*virtual*/void				requestUpdate();
+	/*virtual*/void				requestUpdate() override;
+	void						requestUpload();
+	void						cancelUpload();
 	BOOL						isLocalTextureDataAvailable() const;
 	BOOL						isLocalTextureDataFinal() const;
 	void						updateComposite();
-	/*virtual*/void				createComposite();
+	/*virtual*/void				createComposite() override;
 	void						setUpdatesEnabled(BOOL b);
 	BOOL						getUpdatesEnabled()	const 	{ return mUpdatesEnabled; }
 
@@ -78,12 +80,12 @@ class LLViewerTexLayerSetBuffer final : public LLTexLayerSetBuffer, public LLVie
 	virtual ~LLViewerTexLayerSetBuffer();
 
 public:
-	/*virtual*/ S8          getType() const;
+	/*virtual*/ S8          getType() const override;
 	BOOL					isInitialized(void) const;
 	static void				dumpTotalByteCount();
 	const std::string		dumpTextureInfo() const;
-	virtual void 			restoreGLTexture();
-	virtual void 			destroyGLTexture();
+	void 			restoreGLTexture() override;
+	void 			destroyGLTexture() override;
 private:
 	LLViewerTexLayerSet*	getViewerTexLayerSet() 
 		{ return dynamic_cast<LLViewerTexLayerSet*> (mTexLayerSet); }
@@ -94,25 +96,51 @@ class LLViewerTexLayerSetBuffer final : public LLTexLayerSetBuffer, public LLVie
 	//--------------------------------------------------------------------
 	// Tex Layer Render
 	//--------------------------------------------------------------------
-	virtual void			preRenderTexLayerSet();
-	virtual void			midRenderTexLayerSet(BOOL success);
-	virtual void			postRenderTexLayerSet(BOOL success);
-	virtual S32				getCompositeOriginX() const { return getOriginX(); }
-	virtual S32				getCompositeOriginY() const { return getOriginY(); }
-	virtual S32				getCompositeWidth() const { return getFullWidth(); }
-	virtual S32				getCompositeHeight() const { return getFullHeight(); }
+	void			preRenderTexLayerSet() override;
+	void			midRenderTexLayerSet(BOOL success, LLRenderTarget* bound_target) override;
+	void			postRenderTexLayerSet(BOOL success) override;
+	S32				getCompositeOriginX() const override { return getOriginX(); }
+	S32				getCompositeOriginY() const override { return getOriginY(); }
+	S32				getCompositeWidth() const override { return getFullWidth(); }
+	S32				getCompositeHeight() const override { return getFullHeight(); }
 
 	//--------------------------------------------------------------------
 	// Dynamic Texture Interface
 	//--------------------------------------------------------------------
 public:
-	/*virtual*/ BOOL		needsRender();
+	/*virtual*/ BOOL		needsRender() override;
 protected:
 	// Pass these along for tex layer rendering.
-	virtual void			preRender(BOOL clear_depth) { preRenderTexLayerSet(); }
-	virtual void			postRender(BOOL success) { postRenderTexLayerSet(success); }
-	virtual BOOL			render() { return renderTexLayerSet(mBoundTarget); }
-	
+	void			preRender(BOOL clear_depth) override { preRenderTexLayerSet(); }
+	void			postRender(BOOL success) override { postRenderTexLayerSet(success); }
+	BOOL			render() override { return renderTexLayerSet(mBoundTarget); }
+
+
+	//--------------------------------------------------------------------
+	// Uploads
+	//--------------------------------------------------------------------
+public:
+	void					requestUpload();
+	void					cancelUpload();
+	BOOL					uploadNeeded() const; 			// We need to upload a new texture
+	BOOL					uploadInProgress() const; 		// We have started uploading a new texture and are awaiting the result
+	BOOL					uploadPending() const; 			// We are expecting a new texture to be uploaded at some point
+	static void				onTextureUploadComplete(const LLUUID& uuid,
+													void* userdata,
+													S32 result, LLExtStat ext_status);
+protected:
+	BOOL					isReadyToUpload() const;
+	void					doUpload(LLRenderTarget* bound_target); 					// Does a read back and upload.
+	void					conditionalRestartUploadTimer();
+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
 	//--------------------------------------------------------------------
@@ -129,5 +157,24 @@ class LLViewerTexLayerSetBuffer final : public LLTexLayerSetBuffer, public LLVie
 	LLFrameTimer    		mNeedsUpdateTimer; 				// Tracks time since update was requested and performed.
 };
 
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLBakedUploadData
+//
+// Used by LLTexLayerSetBuffer for a callback.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+struct LLBakedUploadData
+{
+	LLBakedUploadData(const LLVOAvatarSelf* avatar,
+					  LLViewerTexLayerSet* layerset, 
+					  const LLUUID& id,
+					  bool highest_res);
+	~LLBakedUploadData() = default;
+	const LLUUID				mID;
+	const LLVOAvatarSelf*		mAvatar; // note: backlink only; don't LLPointer 
+	LLViewerTexLayerSet*		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_VIEWER_TEXLAYER_H
 
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 2f3584ca238..3c0575f4dfd 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -1076,7 +1076,8 @@ LLViewerFetchedTexture::LLViewerFetchedTexture(const LLUUID& id, FTType f_type,
 	mFTType = f_type;
 	if (mFTType == FTT_HOST_BAKE)
 	{
-		LL_WARNS() << "Unsupported fetch type " << mFTType << LL_ENDL;
+		//LL_WARNS() << "Unsupported fetch type " << mFTType << LL_ENDL;
+		mCanUseHTTP = false;
 	}
 	generateGLTexture();
 	mGLTexturep->setNeedsAlphaAndPickMask(TRUE);
diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp
index abb3a4fa494..8f11eb0aabe 100644
--- a/indra/newview/llviewerwearable.cpp
+++ b/indra/newview/llviewerwearable.cpp
@@ -266,7 +266,7 @@ void LLViewerWearable::setParamsToDefaults()
 	{
 		if( (((LLViewerVisualParam*)param)->getWearableType() == mType ) && (param->isTweakable() ) )
 		{
-			setVisualParamWeight(param->getID(),param->getDefaultWeight());
+			setVisualParamWeight(param->getID(),param->getDefaultWeight(), FALSE);
 		}
 	}
 }
@@ -351,14 +351,14 @@ void LLViewerWearable::writeToAvatar(LLAvatarAppearance *avatarp)
 	ESex new_sex = avatarp->getSex();
 	if( old_sex != new_sex )
 	{
-		viewer_avatar->updateSexDependentLayerSets();
+		viewer_avatar->updateSexDependentLayerSets(FALSE);
 	}	
 }
 
 
 // Updates the user's avatar's appearance, replacing this wearables' parameters and textures with default values.
 // static 
-void LLViewerWearable::removeFromAvatar( LLWearableType::EType type)
+void LLViewerWearable::removeFromAvatar( LLWearableType::EType type, BOOL upload_bake)
 {
 	if (!isAgentAvatarValid()) return;
 
@@ -377,7 +377,7 @@ void LLViewerWearable::removeFromAvatar( LLWearableType::EType type)
 		if( (((LLViewerVisualParam*)param)->getWearableType() == type) && (param->isTweakable() ) )
 		{
 			S32 param_id = param->getID();
-			gAgentAvatarp->setVisualParamWeight( param_id, param->getDefaultWeight());
+			gAgentAvatarp->setVisualParamWeight( param_id, param->getDefaultWeight(), upload_bake );
 		}
 	}
 
@@ -387,7 +387,7 @@ void LLViewerWearable::removeFromAvatar( LLWearableType::EType type)
 	}
 
 	gAgentAvatarp->updateVisualParams();
-	gAgentAvatarp->wearableUpdated(type);
+	gAgentAvatarp->wearableUpdated(type, FALSE);
 }
 
 // Does not copy mAssetID.
@@ -501,6 +501,12 @@ void LLViewerWearable::refreshName()
 	}
 }
 
+void LLViewerWearable::addToBakedTextureHash(LLMD5& hash) const
+{
+	LLUUID asset_id = getAssetID();
+	hash.update((const unsigned char*)asset_id.mData, UUID_BYTES);
+}
+
 struct LLWearableSaveData
 {
 	LLWearableType::EType mType;
diff --git a/indra/newview/llviewerwearable.h b/indra/newview/llviewerwearable.h
index cc99f6af2f7..47afa8f22df 100644
--- a/indra/newview/llviewerwearable.h
+++ b/indra/newview/llviewerwearable.h
@@ -62,8 +62,8 @@ class LLViewerWearable : public LLWearable
 	BOOL				isOldVersion() const;
 
 	/*virtual*/ void	writeToAvatar(LLAvatarAppearance *avatarp);
-	void				removeFromAvatar()	{ LLViewerWearable::removeFromAvatar( mType); }
-	static void			removeFromAvatar( LLWearableType::EType type); 
+	void				removeFromAvatar( BOOL upload_bake )	{ LLViewerWearable::removeFromAvatar( mType, upload_bake); }
+	static void			removeFromAvatar( LLWearableType::EType type, BOOL upload_bake );
 
 	/*virtual*/ EImportResult	importStream( std::istream& input_stream, LLAvatarAppearance* avatarp );
 	
@@ -93,7 +93,7 @@ class LLViewerWearable : public LLWearable
 	// the wearable was worn. make sure the name of the wearable object matches the LLViewerInventoryItem,
 	// not the wearable asset itself.
 	void				refreshName();
-	/*virtual*/void		addToBakedTextureHash(LLMD5& hash) const {}
+	/*virtual*/void		addToBakedTextureHash(LLMD5& hash) const;
 
 protected:
 	LLAssetID			mAssetID;
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 94aa251d000..289e26deb8f 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -682,6 +682,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
 	mLastRezzedStatus(-1),
 	mIsEditingAppearance(FALSE),
 	mUseLocalAppearance(FALSE),
+	mUseServerBakes(FALSE),
 	mLastUpdateRequestCOFVersion(-1),
 	mLastUpdateReceivedCOFVersion(-1),
 	mCachedMuteListUpdateTime(0),
@@ -1086,7 +1087,7 @@ void LLVOAvatar::restoreGL()
 	gAgentAvatarp->setCompositeUpdatesEnabled(TRUE);
 	for (U32 i = 0; i < gAgentAvatarp->mBakedTextureDatas.size(); i++)
 	{
-		gAgentAvatarp->invalidateComposite(gAgentAvatarp->getTexLayerSet(i));
+		gAgentAvatarp->invalidateComposite(gAgentAvatarp->getTexLayerSet(i), FALSE);
 	}
 	gAgentAvatarp->updateMeshTextures();
 }
@@ -2092,7 +2093,7 @@ void LLVOAvatar::applyDefaultParams()
 
 		U8 value = it->second;
 		F32 newWeight = U8_to_F32(value, param->getMinWeight(), param->getMaxWeight());
-		param->setWeight(newWeight);
+		param->setWeight(newWeight, FALSE); // Most likely FALSE is correct here because it's used in resetSkeleton, which is a local operation
 	}
 }
 
@@ -2474,17 +2475,20 @@ LLViewerFetchedTexture *LLVOAvatar::getBakedTextureImage(const U8 te, const LLUU
 	if (!result)
 	{
 		const std::string url = getImageURL(te,uuid);
-
-		if (url.empty())
+		if (!url.empty())
 		{
-			LL_WARNS() << "unable to determine URL for te " << te << " uuid " << uuid << LL_ENDL;
-			return NULL;
+			LL_DEBUGS("Avatar") << avString() << "get server-bake image from URL " << url << LL_ENDL;
+			result = LLViewerTextureManager::getFetchedTextureFromUrl(
+				url, FTT_SERVER_BAKE, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, uuid);
 		}
-#ifdef SHOW_DEBUG
-		LL_DEBUGS("Avatar") << avString() << "get server-bake image from URL " << url << LL_ENDL;
-#endif
-		result = LLViewerTextureManager::getFetchedTextureFromUrl(
-			url, FTT_SERVER_BAKE, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, uuid);
+		else
+		{
+			LL_DEBUGS("Avatar") << avString() << "get old-bake image from host " << uuid << LL_ENDL;
+			LLHost host = getObjectHost();
+			result = LLViewerTextureManager::getFetchedTexture(
+				uuid, FTT_HOST_BAKE, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, host);
+		}
+
 		if (result->isMissingAsset())
 		{
 			result->setIsMissingAsset(false);
@@ -2770,8 +2774,8 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)
 				
 				if ( mLipSyncActive )
 				{
-					if( mOohMorph ) mOohMorph->setWeight(mOohMorph->getMinWeight());
-					if( mAahMorph ) mAahMorph->setWeight(mAahMorph->getMinWeight());
+					if( mOohMorph ) mOohMorph->setWeight(mOohMorph->getMinWeight(), FALSE);
+					if( mAahMorph ) mAahMorph->setWeight(mAahMorph->getMinWeight(), FALSE);
 					
 					mLipSyncActive = false;
 					LLCharacter::updateVisualParams();
@@ -2947,10 +2951,14 @@ void LLVOAvatar::idleUpdateAppearanceAnimation()
 			{
 				if (param->isTweakable())
 				{
-					param->stopAnimating();
+					param->stopAnimating(FALSE);
 				}
 			}
 			updateVisualParams();
+			if (isSelf())
+			{
+				gAgent.sendAgentSetAppearance();
+			}
 		}
 		else
 		{
@@ -2966,7 +2974,7 @@ void LLVOAvatar::idleUpdateAppearanceAnimation()
 				{
 					if (param->isTweakable())
 					{
-						param->animate(morph_amt);
+						param->animate(morph_amt, FALSE);
 					}
 				}
 			}
@@ -3023,7 +3031,7 @@ void LLVOAvatar::idleUpdateLipSync(bool voice_enabled)
 			F32 ooh_weight = mOohMorph->getMinWeight()
 				+ ooh_morph_amount * (mOohMorph->getMaxWeight() - mOohMorph->getMinWeight());
 
-			mOohMorph->setWeight( ooh_weight);
+			mOohMorph->setWeight( ooh_weight, FALSE);
 		}
 
 		if( mAahMorph )
@@ -3031,7 +3039,7 @@ void LLVOAvatar::idleUpdateLipSync(bool voice_enabled)
 			F32 aah_weight = mAahMorph->getMinWeight()
 				+ aah_morph_amount * (mAahMorph->getMaxWeight() - mAahMorph->getMinWeight());
 
-			mAahMorph->setWeight( aah_weight);
+			mAahMorph->setWeight( aah_weight, FALSE);
 		}
 
 		mLipSyncActive = true;
@@ -3839,7 +3847,7 @@ void LLVOAvatar::updateAppearanceMessageDebugText()
 										  isSelf() ? (all_local_downloaded ? "L" : "l") : "-",
 										  all_baked_downloaded ? "B" : "b",
 										  mUseLocalAppearance, mIsEditingAppearance,
-										  1, central_bake_version);
+										  mUseServerBakes, central_bake_version);
 		std::string origin_string = bakedTextureOriginInfo();
 		debug_line += " [" + origin_string + "]";
 		S32 curr_cof_version = LLAppearanceMgr::instanceFast().getCOFVersion();
@@ -3849,7 +3857,9 @@ void LLVOAvatar::updateAppearanceMessageDebugText()
 		{
 			debug_line += llformat(" - cof: %d req: %d rcv:%d",
 								   curr_cof_version, last_request_cof_version, last_received_cof_version);
-			if (gSavedSettings.getBOOL("DebugForceAppearanceRequestFailure"))
+
+			static const LLCachedControl<bool> debug_force_appearance_request_failure(gSavedSettings, "DebugForceAppearanceRequestFailure");
+			if (debug_force_appearance_request_failure)
 			{
 				debug_line += " FORCING ERRS";
 			}
@@ -5475,6 +5485,34 @@ bool LLVOAvatar::allBakedTexturesCompletelyDownloaded() const
 	return allTexturesCompletelyDownloaded(baked_ids);
 }
 
+void LLVOAvatar::bakedTextureOriginCounts(S32 &sb_count, // server-bake, has origin URL.
+										  S32 &host_count, // host-based bake, has host.
+										  S32 &both_count, // error - both host and URL set.
+										  S32 &neither_count) // error - neither set.
+{
+	sb_count = host_count = both_count = neither_count = 0;
+	
+	std::set<LLUUID> baked_ids;
+	collectBakedTextureUUIDs(baked_ids);
+	for (std::set<LLUUID>::const_iterator it = baked_ids.begin(); it != baked_ids.end(); ++it)
+	{
+		LLViewerFetchedTexture *imagep = gTextureList.findImage(*it, TEX_LIST_STANDARD);
+		bool has_url = false, has_host = false;
+		if (!imagep->getUrl().empty())
+		{
+			has_url = true;
+		}
+		if (imagep->getTargetHost().isOk())
+		{
+			has_host = true;
+		}
+		if (has_url && !has_host) sb_count++;
+		else if (has_host && !has_url) host_count++;
+		else if (has_host && has_url) both_count++;
+		else if (!has_host && !has_url) neither_count++;
+	}
+}
+
 std::string LLVOAvatar::bakedTextureOriginInfo()
 {
 	std::string result;
@@ -5715,6 +5753,19 @@ void LLVOAvatar::updateTextures()
 		{
 			const S32 boost_level = getAvatarBakedBoostLevel();
 			imagep = LLViewerTextureManager::staticCastToFetchedTexture(getImage(texture_index,0), TRUE);
+			// Spam if this is a baked texture, not set to default image, without valid host info
+			if (isIndexBakedTexture((ETextureIndex)texture_index)
+				&& imagep->getID() != IMG_DEFAULT_AVATAR
+				&& imagep->getID() != IMG_INVISIBLE
+				&& !isUsingServerBakes() 
+				&& !imagep->getTargetHost().isOk())
+			{
+				LL_WARNS_ONCE("Texture") << "LLVOAvatar::updateTextures No host for texture "
+										 << imagep->getID() << " for avatar "
+										 << (isSelf() ? "<myself>" : getID().asString()) 
+										 << " on host " << getRegion()->getHost() << LL_ENDL;
+			}
+
 			addBakedTextureStats( imagep, mPixelArea, texel_area_ratio, boost_level );			
 		}
 	}
@@ -5868,19 +5919,22 @@ const std::string LLVOAvatar::getImageURL(const U8 te, const LLUUID &uuid)
 {
 	llassert(isIndexBakedTexture(ETextureIndex(te)));
 	std::string url = "";
-	const std::string& appearance_service_url = LLAppearanceMgr::instanceFast().getAppearanceServiceURL();
-	if (appearance_service_url.empty())
+	if (isUsingServerBakes())
 	{
-		// Probably a server-side issue if we get here:
-		LL_WARNS() << "AgentAppearanceServiceURL not set - Baked texture requests will fail" << LL_ENDL;
-		return url;
-	}
+		const std::string& appearance_service_url = LLAppearanceMgr::instanceFast().getAppearanceServiceURL();
+		if (appearance_service_url.empty())
+		{
+			// Probably a server-side issue if we get here:
+			LL_WARNS() << "AgentAppearanceServiceURL not set - Baked texture requests will fail" << LL_ENDL;
+			return url;
+		}
 	
-	const LLAvatarAppearanceDictionary::TextureEntry* texture_entry = LLAvatarAppearance::getDictionary()->getTexture((ETextureIndex)te);
-	if (texture_entry != NULL)
-	{
-		url = appearance_service_url + "texture/" + getID().asString() + "/" + texture_entry->mDefaultImageName + "/" + uuid.asString();
-		//LL_INFOS() << "baked texture url: " << url << LL_ENDL;
+		const LLAvatarAppearanceDictionary::TextureEntry* texture_entry = LLAvatarAppearance::getDictionary()->getTexture((ETextureIndex)te);
+		if (texture_entry != NULL)
+		{
+			url = appearance_service_url + "texture/" + getID().asString() + "/" + texture_entry->mDefaultImageName + "/" + uuid.asString();
+			//LL_INFOS() << "baked texture url: " << url << LL_ENDL;
+		}
 	}
 	return url;
 }
@@ -7367,11 +7421,11 @@ BOOL LLVOAvatar::updateGeometry(LLDrawable *drawable)
 //-----------------------------------------------------------------------------
 // updateSexDependentLayerSets()
 //-----------------------------------------------------------------------------
-void LLVOAvatar::updateSexDependentLayerSets()
+void LLVOAvatar::updateSexDependentLayerSets(BOOL upload_bake)
 {
-	invalidateComposite( mBakedTextureDatas[BAKED_HEAD].mTexLayerSet);
-	invalidateComposite( mBakedTextureDatas[BAKED_UPPER].mTexLayerSet);
-	invalidateComposite( mBakedTextureDatas[BAKED_LOWER].mTexLayerSet);
+	invalidateComposite( mBakedTextureDatas[BAKED_HEAD].mTexLayerSet, upload_bake);
+	invalidateComposite( mBakedTextureDatas[BAKED_UPPER].mTexLayerSet, upload_bake);
+	invalidateComposite( mBakedTextureDatas[BAKED_LOWER].mTexLayerSet, upload_bake);
 }
 
 //-----------------------------------------------------------------------------
@@ -8066,7 +8120,7 @@ void LLVOAvatar::rebuildAttachments()
 // [/SL:KB]
 
 // virtual
-void LLVOAvatar::invalidateComposite( LLTexLayerSet* layerset)
+void LLVOAvatar::invalidateComposite( LLTexLayerSet* layerset, BOOL upload_result)
 {
 }
 
@@ -8075,19 +8129,19 @@ void LLVOAvatar::invalidateAll()
 }
 
 // virtual
-void LLVOAvatar::onGlobalColorChanged(const LLTexGlobalColor* global_color)
+void LLVOAvatar::onGlobalColorChanged(const LLTexGlobalColor* global_color, BOOL upload_bake)
 {
 	if (global_color == mTexSkinColor)
 	{
-		invalidateComposite( mBakedTextureDatas[BAKED_HEAD].mTexLayerSet);
-		invalidateComposite( mBakedTextureDatas[BAKED_UPPER].mTexLayerSet);
-		invalidateComposite( mBakedTextureDatas[BAKED_LOWER].mTexLayerSet);
+		invalidateComposite( mBakedTextureDatas[BAKED_HEAD].mTexLayerSet, upload_bake);
+		invalidateComposite( mBakedTextureDatas[BAKED_UPPER].mTexLayerSet, upload_bake);
+		invalidateComposite( mBakedTextureDatas[BAKED_LOWER].mTexLayerSet, upload_bake);
 	}
 	else if (global_color == mTexHairColor)
 	{
-		invalidateComposite( mBakedTextureDatas[BAKED_HEAD].mTexLayerSet);
-		invalidateComposite( mBakedTextureDatas[BAKED_HAIR].mTexLayerSet);
-		
+		invalidateComposite( mBakedTextureDatas[BAKED_HEAD].mTexLayerSet, upload_bake);
+		invalidateComposite( mBakedTextureDatas[BAKED_HAIR].mTexLayerSet, upload_bake);
+
 		// ! BACKWARDS COMPATIBILITY !
 		// Fix for dealing with avatars from viewers that don't bake hair.
 		if (!isTextureDefined(mBakedTextureDatas[BAKED_HAIR].mTextureIndex))
@@ -8106,7 +8160,7 @@ void LLVOAvatar::onGlobalColorChanged(const LLTexGlobalColor* global_color)
 	else if (global_color == mTexEyeColor)
 	{
 		// LL_INFOS() << "invalidateComposite cause: onGlobalColorChanged( eyecolor )" << LL_ENDL; 
-		invalidateComposite( mBakedTextureDatas[BAKED_EYES].mTexLayerSet);
+		invalidateComposite( mBakedTextureDatas[BAKED_EYES].mTexLayerSet, upload_bake);
 	}
 	updateMeshTextures();
 }
@@ -8322,7 +8376,7 @@ void LLVOAvatar::logMetricsTimerRecord(const std::string& phase_name, F32 elapse
 	}
 	record["grid_x"] = LLSD::Integer(grid_x);
 	record["grid_y"] = LLSD::Integer(grid_y);
-	record["is_using_server_bakes"] = true;
+	record["is_using_server_bakes"] = ((bool) isUsingServerBakes());
 	record["is_self"] = isSelf();
 		
 	if (isAgentAvatarValid())
@@ -9363,13 +9417,13 @@ bool resolve_appearance_version(const LLAppearanceMessageContents& contents, S32
 	{
 		appearance_version = contents.mParamAppearanceVersion;
 	}
-	else if (contents.mAppearanceVersion > 0)
+	if (contents.mAppearanceVersion >= 0)
 	{
 		appearance_version = contents.mAppearanceVersion;
 	}
-	else // still not set, go with 1.
+	if (appearance_version < 0) // still not set, go with 0.
 	{
-		appearance_version = 1;
+		appearance_version = 0;
 	}
 	//LL_DEBUGS("Avatar") << "appearance version info - field " << contents.mAppearanceVersion
 	//					<< " param: " << contents.mParamAppearanceVersion
@@ -9428,6 +9482,11 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
             " (highest seen #" << mLastUpdateReceivedCOFVersion <<
             ") (AISCOF=#" << aisCOFVersion << ")" << LL_ENDL;
 
+        if (mFirstTEMessageReceived && (appearance_version == 0))
+        {
+            return;
+        }
+
         if (mLastUpdateReceivedCOFVersion >= thisAppearanceVersion)
         {
             LL_WARNS("Avatar") << "Stale appearance received #" << thisAppearanceVersion <<
@@ -9478,6 +9537,8 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
 
     mLastProcessedAppearance = contents;
 
+    setIsUsingServerBakes(appearance_version > 0);
+
     bool slam_params = false;
 	applyParsedAppearanceMessage(*contents, slam_params);
 	if (getOverallAppearance() != AOA_NORMAL)
@@ -9503,16 +9564,14 @@ void LLVOAvatar::applyParsedAppearanceMessage(LLAppearanceMessageContents& conte
 			&& mBakedTextureDatas[baked_index].mLastTextureID != IMG_DEFAULT
 			&& baked_index != BAKED_SKIRT && baked_index != BAKED_LEFT_ARM && baked_index != BAKED_LEFT_LEG && baked_index != BAKED_AUX1 && baked_index != BAKED_AUX2 && baked_index != BAKED_AUX3)
 		{
-#ifdef SHOW_DEBUG
-			LL_DEBUGS("Avatar") << avString() << " baked_index " << (S32) baked_index << " using mLastTextureID " << mBakedTextureDatas[baked_index].mLastTextureID << LL_ENDL;
-#endif
+			LL_DEBUGS("Avatar") << avString() << "sb " << (S32) isUsingServerBakes() << " baked_index " << (S32) baked_index << " using mLastTextureID " << mBakedTextureDatas[baked_index].mLastTextureID << LL_ENDL;
 			setTEImage(mBakedTextureDatas[baked_index].mTextureIndex, 
 				LLViewerTextureManager::getFetchedTexture(mBakedTextureDatas[baked_index].mLastTextureID, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE));
 		}
 #ifdef SHOW_DEBUG
 		else
 		{
-			LL_DEBUGS("Avatar") << avString() << " baked_index " << (S32) baked_index << " using texture id "
+			LL_DEBUGS("Avatar") << avString() << "sb " << (S32) isUsingServerBakes() << " baked_index " << (S32) baked_index << " using texture id "
 								<< getTE(mBakedTextureDatas[baked_index].mTextureIndex)->getID() << LL_ENDL;
 		}
 #endif
@@ -9556,12 +9615,12 @@ void LLVOAvatar::applyParsedAppearanceMessage(LLAppearanceMessageContents& conte
 				if(is_first_appearance_message || slam_params)
 				{
 					//LL_DEBUGS("Avatar") << "param slam " << i << " " << newWeight << LL_ENDL;
-					param->setWeight(newWeight);
+					param->setWeight(newWeight, FALSE);
 				}
 				else
 				{
 					interp_params = TRUE;
-					param->setAnimationTarget(newWeight);
+					param->setAnimationTarget(newWeight, FALSE);
 				}
 			}
 		}
@@ -9588,7 +9647,7 @@ void LLVOAvatar::applyParsedAppearanceMessage(LLAppearanceMessageContents& conte
 			ESex new_sex = getSex();
 			if( old_sex != new_sex )
 			{
-				updateSexDependentLayerSets();
+				updateSexDependentLayerSets(FALSE);
 			}	
 		}
 
@@ -10314,6 +10373,39 @@ void LLVOAvatar::startAppearanceAnimation()
 	}
 }
 
+// virtual
+void LLVOAvatar::bodySizeChanged()
+{
+	if (isSelf() && !LLAppearanceMgr::instance().isInUpdateAppearanceFromCOF())
+	{	// notify simulator of change in size
+		// but not if we are in the middle of updating appearance
+		gAgent.sendAgentSetAppearance();
+	}
+}
+
+BOOL LLVOAvatar::isUsingServerBakes() const
+{
+	// Sanity check - visual param for appearance version should match mUseServerBakes
+	LLVisualParam* appearance_version_param = getVisualParam(11000);
+	llassert(appearance_version_param);
+	F32 wt = appearance_version_param->getWeight();
+	F32 expect_wt = mUseServerBakes ? 1.f : 0.f;
+	if (!is_approx_equal(wt, expect_wt))
+	{
+		LL_WARNS() << "wt " << wt << " differs from expected " << expect_wt << LL_ENDL;
+	}
+
+	return mUseServerBakes;
+}
+
+void LLVOAvatar::setIsUsingServerBakes(BOOL newval)
+{
+	mUseServerBakes = newval;
+	LLVisualParam* appearance_version_param = getVisualParam(11000);
+	llassert(appearance_version_param);
+	appearance_version_param->setWeight(newval ? 1.f : 0.f, false);
+}
+
 // virtual
 void LLVOAvatar::removeMissingBakedTextures()
 {
@@ -10560,7 +10652,6 @@ void LLVOAvatar::updateRiggingInfo()
 #endif
 }
 
-// virtual
 void LLVOAvatar::onActiveOverrideMeshesChanged()
 {
     mJointRiggingInfoTab.setNeedsUpdate(true);
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 50e2faa60ad..bfa6802cbfc 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -234,7 +234,7 @@ class LLVOAvatar :
     size_t mLastRiggingInfoMeshCount = 0;
 	
     std::set<LLUUID>		mActiveOverrideMeshes;
-    virtual void			onActiveOverrideMeshesChanged();
+    void			onActiveOverrideMeshesChanged();
     
 	/*virtual*/ const LLUUID&	getID() const;
 	/*virtual*/ void			addDebugText(const std::string& text);
@@ -552,7 +552,7 @@ class LLVOAvatar :
 	// Global colors
 	//--------------------------------------------------------------------
 public:
-	/*virtual*/void onGlobalColorChanged(const LLTexGlobalColor* global_color);
+	/*virtual*/void onGlobalColorChanged(const LLTexGlobalColor* global_color, BOOL upload_bake) override;
 
 	//--------------------------------------------------------------------
 	// Visibility
@@ -728,7 +728,7 @@ class LLVOAvatar :
 	// Composites
 	//--------------------------------------------------------------------
 public:
-	virtual void	invalidateComposite(LLTexLayerSet* layerset);
+	void	invalidateComposite(LLTexLayerSet* layerset, BOOL upload_result) override;
 	virtual void	invalidateAll();
 	virtual void	setCompositeUpdatesEnabled(bool b) {}
 	virtual void 	setCompositeUpdatesEnabled(U32 index, bool b) {}
@@ -761,9 +761,9 @@ class LLVOAvatar :
 
 public:
 	void			debugColorizeSubMeshes(U32 i, const LLColor4& color);
-	virtual void 	updateMeshTextures();
-	void 			updateSexDependentLayerSets();
-	virtual void	dirtyMesh(); // Dirty the avatar mesh
+	void 	updateMeshTextures() final override;
+	void 			updateSexDependentLayerSets(BOOL upload_bake);
+	void	dirtyMesh() final override; // Dirty the avatar mesh
 	void 			updateMeshData();
 	void			updateMeshVisibility();
 	LLViewerTexture*		getBakedTexture(const U8 te);
@@ -772,7 +772,7 @@ class LLVOAvatar :
 	void 			releaseMeshData();
 	virtual void restoreMeshData();
 private:
-	virtual void	dirtyMesh(S32 priority); // Dirty the avatar mesh, with priority
+	void	dirtyMesh(S32 priority) final override; // Dirty the avatar mesh, with priority
 	LLViewerJoint*	getViewerJoint(S32 idx);
 	S32 			mDirtyMesh; // 0 -- not dirty, 1 -- morphed, 2 -- LOD
 	BOOL			mMeshTexturesDirty;
@@ -801,6 +801,7 @@ class LLVOAvatar :
     void            applyParsedAppearanceMessage(LLAppearanceMessageContents& contents, bool slam_params);
 	void 			hideSkirt();
 	void			startAppearanceAnimation();
+	/*virtual*/ void bodySizeChanged() override;
 	
 	//--------------------------------------------------------------------
 	// Appearance morphing
@@ -811,11 +812,14 @@ class LLVOAvatar :
 	// True if we are computing our appearance via local compositing
 	// instead of baked textures, as for example during wearable
 	// editing or when waiting for a subsequent server rebake.
-	/*virtual*/ BOOL	isUsingLocalAppearance() const { return mUseLocalAppearance; }
+	/*virtual*/ BOOL	isUsingLocalAppearance() const override { return mUseLocalAppearance; }
+
+	BOOL				isUsingServerBakes() const override;
+	void 				setIsUsingServerBakes(BOOL newval);
 
 	// True if we are currently in appearance editing mode. Often but
 	// not always the same as isUsingLocalAppearance().
-	/*virtual*/ BOOL	isEditingAppearance() const { return mIsEditingAppearance; }
+	/*virtual*/ BOOL	isEditingAppearance() const override { return mIsEditingAppearance; }
 
 	// FIXME review isUsingLocalAppearance uses, some should be isEditing instead.
 
@@ -825,6 +829,7 @@ class LLVOAvatar :
 	F32				mLastAppearanceBlendTime;
 	BOOL			mIsEditingAppearance; // flag for if we're actively in appearance editing mode
 	BOOL			mUseLocalAppearance; // flag for if we're using a local composite
+	BOOL			mUseServerBakes; // flag for if baked textures should be fetched from baking service (false if they're temporary uploads)
 
 	//--------------------------------------------------------------------
 	// Visibility
@@ -993,9 +998,9 @@ class LLVOAvatar :
  **/
 
 public:
-	/*virtual*/ BOOL 	setParent(LLViewerObject* parent);
-	/*virtual*/ void 	addChild(LLViewerObject *childp);
-	/*virtual*/ void 	removeChild(LLViewerObject *childp);
+	/*virtual*/ BOOL 	setParent(LLViewerObject* parent) override;
+	/*virtual*/ void 	addChild(LLViewerObject *childp) override;
+	/*virtual*/ void 	removeChild(LLViewerObject *childp) override;
 
 	//--------------------------------------------------------------------
 	// Sitting
diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp
index e52f4a40db3..ee28b394897 100644
--- a/indra/newview/llvoavatarself.cpp
+++ b/indra/newview/llvoavatarself.cpp
@@ -197,6 +197,15 @@ bool update_avatar_rez_metrics()
 	return false;
 }
 
+bool check_for_unsupported_baked_appearance()
+{
+	if (!isAgentAvatarValid())
+		return true;
+
+	gAgentAvatarp->checkForUnsupportedServerBakeAppearance();
+	return false;
+}
+
 void LLVOAvatarSelf::initInstance()
 {
 	BOOL status = TRUE;
@@ -238,6 +247,7 @@ void LLVOAvatarSelf::initInstance()
 
 	//doPeriodically(output_self_av_texture_diagnostics, 30.0);
 	doPeriodically(update_avatar_rez_metrics, 5.0);
+	doPeriodically(check_for_unsupported_baked_appearance, 120.0);
 	doPeriodically(boost::bind(&LLVOAvatarSelf::checkStuckAppearance, this), 30.0);
 
     mInitFlags |= 1<<2;
@@ -245,14 +255,19 @@ void LLVOAvatarSelf::initInstance()
 
 void LLVOAvatarSelf::setHoverIfRegionEnabled()
 {
-	if (getRegion() && getRegion()->simulatorFeaturesReceived())
+	LLViewerRegion* region = getRegion();
+	if (region && region->simulatorFeaturesReceived())
 	{
-		if (getRegion()->avatarHoverHeightEnabled())
+		if (region->avatarHoverHeightEnabled())
 		{
 			F32 hover_z = gSavedPerAccountSettings.getF32("AvatarHoverOffsetZ");
 			setHoverOffset(LLVector3(0.0, 0.0, llclamp(hover_z,MIN_HOVER_Z,MAX_HOVER_Z)));
 			LL_INFOS("Avatar") << avString() << " set hover height from debug setting " << hover_z << LL_ENDL;
 		}
+		else if (!isUsingServerBakes())
+		{
+			computeBodySize();
+		}
 		else 
 		{
 			setHoverOffset(LLVector3(0.0, 0.0, 0.0));
@@ -262,9 +277,9 @@ void LLVOAvatarSelf::setHoverIfRegionEnabled()
 	else
 	{
 		LL_INFOS("Avatar") << avString() << " region or simulator features not known, no change on hover" << LL_ENDL;
-		if (getRegion())
+		if (region)
 		{
-			getRegion()->setSimulatorFeaturesReceivedCallback(boost::bind(&LLVOAvatarSelf::onSimulatorFeaturesReceived,this,_1));
+			region->setSimulatorFeaturesReceivedCallback(boost::bind(&LLVOAvatarSelf::onSimulatorFeaturesReceived, this, _1));
 		}
 
 	}
@@ -735,35 +750,35 @@ LLJoint *LLVOAvatarSelf::getJoint(const std::string &name)
 }
 
 // virtual
-BOOL LLVOAvatarSelf::setVisualParamWeight(const LLVisualParam *which_param, F32 weight)
+BOOL LLVOAvatarSelf::setVisualParamWeight(const LLVisualParam *which_param, F32 weight, BOOL upload_bake)
 {
 	if (!which_param)
 	{
 		return FALSE;
 	}
 	LLViewerVisualParam *param = (LLViewerVisualParam*) LLCharacter::getVisualParam(which_param->getID());
-	return setParamWeight(param,weight);
+	return setParamWeight(param,weight,upload_bake);
 }
 
 // virtual
-BOOL LLVOAvatarSelf::setVisualParamWeight(const char* param_name, F32 weight)
+BOOL LLVOAvatarSelf::setVisualParamWeight(const char* param_name, F32 weight, BOOL upload_bake)
 {
 	if (!param_name)
 	{
 		return FALSE;
 	}
 	LLViewerVisualParam *param = (LLViewerVisualParam*) LLCharacter::getVisualParam(param_name);
-	return setParamWeight(param,weight);
+	return setParamWeight(param,weight,upload_bake);
 }
 
 // virtual
-BOOL LLVOAvatarSelf::setVisualParamWeight(S32 index, F32 weight)
+BOOL LLVOAvatarSelf::setVisualParamWeight(S32 index, F32 weight, BOOL upload_bake)
 {
 	LLViewerVisualParam *param = (LLViewerVisualParam*) LLCharacter::getVisualParam(index);
-	return setParamWeight(param,weight);
+	return setParamWeight(param,weight,upload_bake);
 }
 
-BOOL LLVOAvatarSelf::setParamWeight(const LLViewerVisualParam *param, F32 weight)
+BOOL LLVOAvatarSelf::setParamWeight(const LLViewerVisualParam *param, F32 weight, BOOL upload_bake)
 {
 	if (!param)
 	{
@@ -779,12 +794,12 @@ BOOL LLVOAvatarSelf::setParamWeight(const LLViewerVisualParam *param, F32 weight
 			LLViewerWearable *wearable = gAgentWearables.getViewerWearable(type,count);
 			if (wearable)
 			{
-				wearable->setVisualParamWeight(param->getID(), weight);
+				wearable->setVisualParamWeight(param->getID(), weight, upload_bake);
 			}
 		}
 	}
 
-	return LLCharacter::setVisualParamWeight(param,weight);
+	return LLCharacter::setVisualParamWeight(param,weight,upload_bake);
 }
 
 /*virtual*/ 
@@ -810,7 +825,7 @@ void LLVOAvatarSelf::writeWearablesToAvatar()
 void LLVOAvatarSelf::idleUpdateAppearanceAnimation()
 {
 	// Animate all top-level wearable visual parameters
-	gAgentWearables.animateAllWearableParams(calcMorphAmount());
+	gAgentWearables.animateAllWearableParams(calcMorphAmount(), FALSE);
 
 	// Apply wearable visual params to avatar
 	writeWearablesToAvatar();
@@ -904,18 +919,23 @@ void LLVOAvatarSelf::removeMissingBakedTextures()
 		{
 			LLViewerTexLayerSet *layerset = getTexLayerSet(i);
 			layerset->setUpdatesEnabled(TRUE);
-			invalidateComposite(layerset);
+			invalidateComposite(layerset, FALSE);
 		}
 		updateMeshTextures();
+		if (getRegion() && !getRegion()->getCentralBakeVersion())
+		{
+			requestLayerSetUploads();
+		}
 	}
 }
-// <FS:Beq> Check whether the BOM capability is different to last time we changed region (even across login)
+
 void LLVOAvatarSelf::checkBOMRebakeRequired()
 {
 	if(getRegion())
 	{
 		auto newBOMStatus = getRegion()->bakesOnMeshEnabled();
-		if((!gSavedSettings.getBool("CurrentlyUsingBakesOnMesh")) != newBOMStatus)
+		static const LLCachedControl<bool> using_bom(gSavedSettings, "CurrentlyUsingBakesOnMesh", true);
+		if(!using_bom != newBOMStatus)
 		{
 			// force a rebake when the last grid we were on (including previous login) had different BOM support
 			// This replicates forceAppearanceUpdate rather than pulling in the whole of llavatarself.
@@ -928,13 +948,12 @@ void LLVOAvatarSelf::checkBOMRebakeRequired()
 		}
 	}
 }
-// </FS:Beq>
 
 void LLVOAvatarSelf::onSimulatorFeaturesReceived(const LLUUID& region_id)
 {
 	LL_INFOS("Avatar") << "simulator features received, setting hover based on region props" << LL_ENDL;
 	setHoverIfRegionEnabled();
-	checkBOMRebakeRequired();// <FS:Beq/> BOM we may have stale cache, rebake may be needed
+	checkBOMRebakeRequired(); // BOM we may have stale cache, rebake may be needed
 }
 
 //virtual
@@ -961,7 +980,7 @@ void LLVOAvatarSelf::updateRegion(LLViewerRegion *regionp)
 		if (regionp->simulatorFeaturesReceived())
 		{
 			setHoverIfRegionEnabled();
-			checkBOMRebakeRequired();// <FS:Beq/> BOM we may have stale cache, rebake may be needed
+			checkBOMRebakeRequired();
 		}
 		else
 		{
@@ -1129,7 +1148,7 @@ void LLVOAvatarSelf::updateAttachmentVisibility(U32 camera_mode)
 // forces an update to any baked textures relevant to type.
 // will force an upload of the resulting bake if the second parameter is TRUE
 //-----------------------------------------------------------------------------
-void LLVOAvatarSelf::wearableUpdated(LLWearableType::EType type)
+void LLVOAvatarSelf::wearableUpdated(LLWearableType::EType type, BOOL upload_result)
 {
 	for (const auto& baked_pair : sAvatarDictionary->getBakedTextures())
 	{
@@ -1146,13 +1165,20 @@ void LLVOAvatarSelf::wearableUpdated(LLWearableType::EType type)
 					if (layerset)
 					{
 						layerset->setUpdatesEnabled(true);
-						invalidateComposite(layerset);
+						invalidateComposite(layerset, upload_result);
 					}
 					break;
 				}
 			}
 		}
 	}
+
+	// Physics type has no associated baked textures, but change of params needs to be sent to
+	// other avatars.
+	if (type == LLWearableType::WT_PHYSICS)
+	{
+		gAgent.sendAgentSetAppearance();
+	}
 }
 
 //-----------------------------------------------------------------------------
@@ -1581,6 +1607,15 @@ BOOL LLVOAvatarSelf::isAllLocalTextureDataFinal() const
 	return TRUE;
 }
 
+BOOL LLVOAvatarSelf::isBakedTextureFinal(const LLAvatarAppearanceDefines::EBakedTextureIndex index) const
+{
+	const LLViewerTexLayerSet *layerset = getLayerSet(index);
+	if (!layerset) return FALSE;
+	const LLViewerTexLayerSetBuffer *layerset_buffer = layerset->getViewerComposite();
+	if (!layerset_buffer) return FALSE;
+	return !layerset_buffer->uploadNeeded();
+}
+
 BOOL LLVOAvatarSelf::isTextureDefined(LLAvatarAppearanceDefines::ETextureIndex type, U32 index) const
 {
 	LLUUID id;
@@ -1646,12 +1681,48 @@ BOOL LLVOAvatarSelf::isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex t
 	}
 }
 
+//-----------------------------------------------------------------------------
+// requestLayerSetUploads()
+//-----------------------------------------------------------------------------
+void LLVOAvatarSelf::requestLayerSetUploads()
+{
+	for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
+	{
+		requestLayerSetUpload((EBakedTextureIndex)i);
+	}
+}
+
+void LLVOAvatarSelf::requestLayerSetUpload(LLAvatarAppearanceDefines::EBakedTextureIndex i)
+{
+	ETextureIndex tex_index = mBakedTextureDatas[i].mTextureIndex;
+	const BOOL layer_baked = isTextureDefined(tex_index, gAgentWearables.getWearableCount(tex_index));
+	LLViewerTexLayerSet *layerset = getLayerSet(i);
+	if (!layer_baked && layerset)
+	{
+		layerset->requestUpload();
+	}
+}
+
 bool LLVOAvatarSelf::areTexturesCurrent() const
 {
-	return gAgentWearables.areWearablesLoaded();
+	return !hasPendingBakedUploads() && gAgentWearables.areWearablesLoaded();
+}
+
+// virtual
+bool LLVOAvatarSelf::hasPendingBakedUploads() const
+{
+	for (U32 i = 0; i < getNumBakes(); i++)
+	{
+		LLViewerTexLayerSet* layerset = getTexLayerSet(i);
+		if (layerset && layerset->getViewerComposite() && layerset->getViewerComposite()->uploadPending())
+		{
+			return true;
+		}
+	}
+	return false;
 }
 
-void LLVOAvatarSelf::invalidateComposite( LLTexLayerSet* layerset)
+void LLVOAvatarSelf::invalidateComposite( LLTexLayerSet* layerset, BOOL upload_result)
 {
 	LLViewerTexLayerSet *layer_set = dynamic_cast<LLViewerTexLayerSet*>(layerset);
 	if( !layer_set || !layer_set->getUpdatesEnabled() )
@@ -1662,6 +1733,16 @@ void LLVOAvatarSelf::invalidateComposite( LLTexLayerSet* layerset)
 
 	layer_set->requestUpdate();
 	layer_set->invalidateMorphMasks();
+
+	if( upload_result  && (getRegion() && !getRegion()->getCentralBakeVersion()))
+	{
+		llassert(isSelf());
+
+		ETextureIndex baked_te = getBakedTE( layer_set );
+		setTEImage( baked_te, LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT_AVATAR) );
+		layer_set->requestUpload();
+		updateMeshTextures();
+	}
 }
 
 void LLVOAvatarSelf::invalidateAll()
@@ -1669,7 +1750,7 @@ void LLVOAvatarSelf::invalidateAll()
 	for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
 	{
 		LLViewerTexLayerSet *layerset = getTexLayerSet(i);
-		invalidateComposite(layerset);
+		invalidateComposite(layerset, TRUE);
 	}
 	//mDebugSelfLoadTimer.reset();
 }
@@ -2224,7 +2305,6 @@ const std::string LLVOAvatarSelf::debugDumpLocalTextureDataInfo(const LLViewerTe
 					for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++)
 					{
 						const U32 discard_level = getLocalDiscardLevel(tex_index, wearable_index);
-						std::string discard_str = llformat("%d ",discard_level);
 						text += llformat("%d ",discard_level);
 					}
 				}
@@ -2428,6 +2508,49 @@ void LLVOAvatarSelf::sendViewerAppearanceChangeMetrics()
 	}
 }
 
+void CheckAgentAppearanceService_httpSuccess( LLSD const &aData )
+{
+		LL_DEBUGS("Avatar") << "OK" << LL_ENDL;
+}
+
+void forceAppearanceUpdate()
+{
+	// Trying to rebake immediately after crossing region boundary
+	// seems to be failure prone; adding a delay factor. Yes, this
+	// fix is ad-hoc and not guaranteed to work in all cases.
+	doAfterInterval(boost::bind(&LLVOAvatarSelf::forceBakeAllTextures,	gAgentAvatarp.get(), true), 5.0);
+}
+
+void CheckAgentAppearanceService_httpFailure( LLSD const &aData )
+{
+	if (isAgentAvatarValid())
+	{
+		LL_DEBUGS("Avatar") << "failed, will rebake " << aData << LL_ENDL;
+		forceAppearanceUpdate();
+	}	
+}
+
+void LLVOAvatarSelf::checkForUnsupportedServerBakeAppearance()
+{
+	// Need to check only if we have a server baked appearance and are
+	// in a non-baking region.
+	if (!gAgentAvatarp->isUsingServerBakes())
+		return;
+	if (!gAgent.getRegion() || gAgent.getRegion()->getCentralBakeVersion()!=0)
+		return;
+
+	// if baked image service is unknown, need to refresh.
+	if (LLAppearanceMgr::instance().getAppearanceServiceURL().empty())
+	{
+		forceAppearanceUpdate();
+	}
+	// query baked image service to check status.
+	std::string image_url = gAgentAvatarp->getImageURL(TEX_HEAD_BAKED,
+													   getTE(TEX_HEAD_BAKED)->getID());
+
+	LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet( image_url, CheckAgentAppearanceService_httpSuccess, CheckAgentAppearanceService_httpFailure );
+}
+
 const LLUUID& LLVOAvatarSelf::grabBakedTexture(EBakedTextureIndex baked_index) const
 {
 	if (canGrabBakedTexture(baked_index))
@@ -2584,6 +2707,81 @@ ETextureIndex LLVOAvatarSelf::getBakedTE( const LLViewerTexLayerSet* layerset )
 	return TEX_HEAD_BAKED;
 }
 
+
+void LLVOAvatarSelf::setNewBakedTexture(LLAvatarAppearanceDefines::EBakedTextureIndex i, const LLUUID &uuid)
+{
+	ETextureIndex index = LLAvatarAppearance::getDictionary()->bakedToLocalTextureIndex(i);
+	setNewBakedTexture(index, uuid);
+}
+
+
+//-----------------------------------------------------------------------------
+// setNewBakedTexture()
+// A new baked texture has been successfully uploaded and we can start using it now.
+//-----------------------------------------------------------------------------
+void LLVOAvatarSelf::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid )
+{
+	// Baked textures live on other sims.
+	LLHost target_host = getObjectHost();	
+	setTEImage( te, LLViewerTextureManager::getFetchedTextureFromHost( uuid, FTT_HOST_BAKE, target_host ) );
+	updateMeshTextures();
+	dirtyMesh();
+
+	LLVOAvatar::cullAvatarsByPixelArea();
+
+	/* switch(te)
+		case TEX_HEAD_BAKED:
+			LL_INFOS() << "New baked texture: HEAD" << LL_ENDL; */
+	const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearance::getDictionary()->getTexture(te);
+	if (texture_dict->mIsBakedTexture)
+	{
+		debugBakedTextureUpload(texture_dict->mBakedTextureIndex, TRUE); // FALSE for start of upload, TRUE for finish.
+		LL_INFOS() << "New baked texture: " << texture_dict->mName << " UUID: " << uuid <<LL_ENDL;
+	}
+	else
+	{
+		LL_WARNS() << "New baked texture: unknown te " << te << LL_ENDL;
+	}
+	
+	//	dumpAvatarTEs( "setNewBakedTexture() send" );
+	// RN: throttle uploads
+	if (!hasPendingBakedUploads())
+	{
+		gAgent.sendAgentSetAppearance();
+
+		if (gSavedSettings.getBOOL("DebugAvatarRezTime"))
+		{
+			LLSD args;
+			args["EXISTENCE"] = llformat("%d",(U32)mDebugExistenceTimer.getElapsedTimeF32());
+			args["TIME"] = llformat("%d",(U32)mDebugSelfLoadTimer.getElapsedTimeF32());
+			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"
+						<< LL_ENDL;
+			}
+			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"
+						<< LL_ENDL;
+			}
+		}
+
+		outputRezDiagnostics();
+	}
+}
 // FIXME: This is not called consistently. Something may be broken.
 void LLVOAvatarSelf::outputRezDiagnostics() const
 {
@@ -2659,6 +2857,76 @@ void LLVOAvatarSelf::reportAvatarRezTime() const
 	// TODO: report mDebugSelfLoadTimer.getElapsedTimeF32() somehow.
 }
 
+//-----------------------------------------------------------------------------
+// setCachedBakedTexture()
+// A baked texture id was received from a cache query, make it active
+//-----------------------------------------------------------------------------
+void LLVOAvatarSelf::setCachedBakedTexture( ETextureIndex te, const LLUUID& uuid )
+{
+	setTETexture( te, uuid );
+
+	/* switch(te)
+		case TEX_HEAD_BAKED:
+			if( mHeadLayerSet )
+				mHeadLayerSet->cancelUpload(); */
+	for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
+	{
+		LLViewerTexLayerSet *layerset = getTexLayerSet(i);
+		if ( mBakedTextureDatas[i].mTextureIndex == te && layerset)
+		{
+			layerset->cancelUpload();
+		}
+	}
+}
+
+// static
+void LLVOAvatarSelf::processRebakeAvatarTextures(LLMessageSystem* msg, void**)
+{
+	LLUUID texture_id;
+	msg->getUUID("TextureData", "TextureID", texture_id);
+	if (!isAgentAvatarValid()) return;
+
+	// If this is a texture corresponding to one of our baked entries, 
+	// just rebake that layer set.
+	BOOL found = FALSE;
+
+	/* ETextureIndex baked_texture_indices[BAKED_NUM_INDICES] =
+			TEX_HEAD_BAKED,
+			TEX_UPPER_BAKED, */
+	for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearance::getDictionary()->getTextures().begin();
+		 iter != LLAvatarAppearance::getDictionary()->getTextures().end();
+		 ++iter)
+	{
+		const ETextureIndex index = iter->first;
+		const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second;
+		if (texture_dict->mIsBakedTexture)
+		{
+			if (texture_id == gAgentAvatarp->getTEImage(index)->getID())
+			{
+				LLViewerTexLayerSet* layer_set = gAgentAvatarp->getLayerSet(index);
+				if (layer_set)
+				{
+					LL_INFOS() << "TAT: rebake - matched entry " << (S32)index << LL_ENDL;
+					gAgentAvatarp->invalidateComposite(layer_set, TRUE);
+					found = TRUE;
+					add(LLStatViewer::TEX_REBAKES, 1);
+				}
+			}
+		}
+	}
+
+	// If texture not found, rebake all entries.
+	if (!found)
+	{
+		gAgentAvatarp->forceBakeAllTextures();
+	}
+	else
+	{
+		// Not sure if this is necessary, but forceBakeAllTextures() does it.
+		gAgentAvatarp->updateMeshTextures();
+	}
+}
+
 // SUNSHINE CLEANUP - not clear we need any of this, may be sufficient to request server appearance in llviewermenu.cpp:handle_rebake_textures()
 void LLVOAvatarSelf::forceBakeAllTextures(bool slam_for_debug)
 {
@@ -2673,9 +2941,10 @@ void LLVOAvatarSelf::forceBakeAllTextures(bool slam_for_debug)
 			if (slam_for_debug)
 			{
 				layer_set->setUpdatesEnabled(TRUE);
+				layer_set->cancelUpload();
 			}
 
-			invalidateComposite(layer_set);
+			invalidateComposite(layer_set, TRUE);
 			add(LLStatViewer::TEX_REBAKES, 1);
 		}
 		else
@@ -2738,9 +3007,6 @@ LLViewerTexLayerSet* LLVOAvatarSelf::getLayerSet(EBakedTextureIndex baked_index)
        return NULL;
 }
 
-
-
-
 // static
 void LLVOAvatarSelf::onCustomizeStart(bool disable_camera_switch)
 {
@@ -2772,6 +3038,12 @@ void LLVOAvatarSelf::onCustomizeEnd(bool disable_camera_switch)
 	if (isAgentAvatarValid())
 	{
 		gAgentAvatarp->mIsEditingAppearance = false;
+		if (gAgentAvatarp->getRegion() && !gAgentAvatarp->getRegion()->getCentralBakeVersion())
+		{
+			// FIXME DRANO - move to sendAgentSetAppearance, make conditional on upload complete.
+			gAgentAvatarp->mUseLocalAppearance = false;
+		}
+
 		gAgentAvatarp->invalidateAll();
 
 		if (gSavedSettings.getBOOL("AppearanceCameraMovement") && !disable_camera_switch)
@@ -2806,7 +3078,6 @@ bool LLVOAvatarSelf::sendAppearanceMessage(LLMessageSystem *mesgsys) const
 	{
 		const ETextureIndex index = tex_pair.first;
 		const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = tex_pair.second;
-		// <FS:Beq> hide the surplus bakes and universals from non-BOM
 		if( (index == TEX_SKIRT || index == TEX_SKIRT_TATTOO) && !gAgentAvatarp->isWearingWearableType(LLWearableType::WT_SKIRT) )
 		{
 			// TODO(BEQ): combine this with clause below once proven it works.
@@ -2816,7 +3087,6 @@ bool LLVOAvatarSelf::sendAppearanceMessage(LLMessageSystem *mesgsys) const
 			entry->setID(IMG_DEFAULT_AVATAR);
 		}
 		if (!texture_dict->mIsBakedTexture || index >= getRegion()->getRegionMaxTEs())
-		// </FS:Beq>
 		{
 			LLTextureEntry* entry = getTE((U8) index);
 			texture_id[index] = entry->getID();
@@ -2831,9 +3101,7 @@ bool LLVOAvatarSelf::sendAppearanceMessage(LLMessageSystem *mesgsys) const
 	{
 		const ETextureIndex index = tex_pair.first;
 		const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = tex_pair.second;
-		// <FS:Beq> hide the surplus bakes and universals from non-BOM
 		if (!texture_dict->mIsBakedTexture || index >= getRegion()->getRegionMaxTEs())
-		// </FS:Beq>
 		{
 			LLTextureEntry* entry = getTE((U8) index);
 			entry->setID(texture_id[index]);
@@ -2952,10 +3220,8 @@ void LLVOAvatarSelf::dumpWearableInfo(LLAPRFile& outfile)
 							 type_name.c_str(), wearable->getName().c_str() );
 			LLWearable::visual_param_vec_t v_params;
 			wearable->getVisualParams(v_params);
-			for (LLWearable::visual_param_vec_t::iterator it = v_params.begin();
-				 it != v_params.end(); ++it)
+			for (LLVisualParam *param : v_params)
 			{
-				LLVisualParam *param = *it;
 				dump_visual_param(file, param, param->getWeight());
 			}
 		}
@@ -3015,4 +3281,4 @@ void LLVOAvatarSelf::dumpWearableInfo(LLAPRFile& outfile)
 //	}
 //	mPendingObjectDetach.clear();
 //}
-//// [/SL:KB]
+//// [/SL:KB]
\ No newline at end of file
diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h
index 2c7e95a2ce5..3bc4c3557d5 100644
--- a/indra/newview/llvoavatarself.h
+++ b/indra/newview/llvoavatarself.h
@@ -55,11 +55,11 @@ class LLVOAvatarSelf final :
 public:
 	LLVOAvatarSelf(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
 	virtual 				~LLVOAvatarSelf();
-	virtual void			markDead();
-	virtual void 			initInstance(); // Called after construction to initialize the class.
+	void			markDead() override;
+	void 			initInstance() override; // Called after construction to initialize the class.
 	void					cleanup();
 protected:
-	/*virtual*/ BOOL		loadAvatar();
+	/*virtual*/ BOOL		loadAvatar() override;
 	BOOL					loadAvatarSelf();
 	BOOL					buildSkeletonSelf(const LLAvatarSkeletonInfo *info);
 	BOOL					buildMenus();
@@ -80,28 +80,28 @@ class LLVOAvatarSelf final :
 	boost::signals2::connection                   mRegionChangedSlot;
 
 	void					onSimulatorFeaturesReceived(const LLUUID& region_id);
-	/*virtual*/ void 		updateRegion(LLViewerRegion *regionp);
-	/*virtual*/ void   	 	idleUpdate(LLAgent &agent, const F64 &time);
+	/*virtual*/ void 		updateRegion(LLViewerRegion *regionp) override;
+	/*virtual*/ void   	 	idleUpdate(LLAgent &agent, const F64 &time) override;
 
 	//--------------------------------------------------------------------
 	// LLCharacter interface and related
 	//--------------------------------------------------------------------
 public:
-	/*virtual*/ bool 		hasMotionFromSource(const LLUUID& source_id);
-	/*virtual*/ void 		stopMotionFromSource(const LLUUID& source_id);
-	/*virtual*/ void 		requestStopMotion(LLMotion* motion);
-	/*virtual*/ LLJoint*	getJoint(const std::string &name);
+	/*virtual*/ bool 		hasMotionFromSource(const LLUUID& source_id) override;
+	/*virtual*/ void 		stopMotionFromSource(const LLUUID& source_id) override;
+	/*virtual*/ void 		requestStopMotion(LLMotion* motion) override;
+	/*virtual*/ LLJoint*	getJoint(const std::string &name) override;
 	
-	/*virtual*/ BOOL setVisualParamWeight(const LLVisualParam *which_param, F32 weight);
-	/*virtual*/ BOOL setVisualParamWeight(const char* param_name, F32 weight);
-	/*virtual*/ BOOL setVisualParamWeight(S32 index, F32 weight);
-	/*virtual*/ void updateVisualParams();
+	/*virtual*/ BOOL setVisualParamWeight(const LLVisualParam *which_param, F32 weight, BOOL upload_bake = FALSE) override;
+	/*virtual*/ BOOL setVisualParamWeight(const char* param_name, F32 weight, BOOL upload_bake = FALSE) override;
+	/*virtual*/ BOOL setVisualParamWeight(S32 index, F32 weight, BOOL upload_bake = FALSE) override;
+	/*virtual*/ void updateVisualParams() override;
 	void writeWearablesToAvatar();
-	/*virtual*/ void idleUpdateAppearanceAnimation();
+	/*virtual*/ void idleUpdateAppearanceAnimation() override;
 
 private:
 	// helper function. Passed in param is assumed to be in avatar's parameter list.
-	BOOL setParamWeight(const LLViewerVisualParam *param, F32 weight);
+	BOOL setParamWeight(const LLViewerVisualParam *param, F32 weight, BOOL upload_bake = FALSE);
 
 /********************************************************************************
  **                                                                            **
@@ -158,9 +158,9 @@ class LLVOAvatarSelf final :
 	// LLVOAvatar Constants
 	//--------------------------------------------------------------------
 public:
-	/*virtual*/ LLViewerTexture::EBoostLevel 	getAvatarBoostLevel() const { return LLGLTexture::BOOST_AVATAR_SELF; }
-	/*virtual*/ LLViewerTexture::EBoostLevel 	getAvatarBakedBoostLevel() const { return LLGLTexture::BOOST_AVATAR_BAKED_SELF; }
-	/*virtual*/ S32 						getTexImageSize() const { return LLVOAvatar::getTexImageSize()*4; }
+	/*virtual*/ LLViewerTexture::EBoostLevel 	getAvatarBoostLevel() const override { return LLGLTexture::BOOST_AVATAR_SELF; }
+	/*virtual*/ LLViewerTexture::EBoostLevel 	getAvatarBakedBoostLevel() const override { return LLGLTexture::BOOST_AVATAR_BAKED_SELF; }
+	/*virtual*/ S32 						getTexImageSize() const override { return LLVOAvatar::getTexImageSize()*4; }
 
 /**                    Rendering
  **                                                                            **
@@ -175,14 +175,16 @@ class LLVOAvatarSelf final :
 	// Loading status
 	//--------------------------------------------------------------------
 public:
+	/*virtual*/ bool	hasPendingBakedUploads() const;
 	S32					getLocalDiscardLevel(LLAvatarAppearanceDefines::ETextureIndex type, U32 index) const;
 	bool				areTexturesCurrent() const;
 	BOOL				isLocalTextureDataAvailable(const LLViewerTexLayerSet* layerset) const;
 	BOOL				isLocalTextureDataFinal(const LLViewerTexLayerSet* layerset) const;
+	BOOL				isBakedTextureFinal(const LLAvatarAppearanceDefines::EBakedTextureIndex index) const;
 	// If you want to check all textures of a given type, pass gAgentWearables.getWearableCount() for index
-	/*virtual*/ BOOL    isTextureDefined(LLAvatarAppearanceDefines::ETextureIndex type, U32 index) const;
-	/*virtual*/ BOOL	isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex type, U32 index = 0) const;
-	/*virtual*/ BOOL	isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex type, LLViewerWearable *wearable) const;
+	/*virtual*/ BOOL    isTextureDefined(LLAvatarAppearanceDefines::ETextureIndex type, U32 index) const override;
+	/*virtual*/ BOOL	isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex type, U32 index = 0) const override;
+	/*virtual*/ BOOL	isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex type, LLViewerWearable *wearable) const override;
 
 
 	//--------------------------------------------------------------------
@@ -193,19 +195,19 @@ class LLVOAvatarSelf final :
 	LLViewerFetchedTexture*	getLocalTextureGL(LLAvatarAppearanceDefines::ETextureIndex type, U32 index) const;
 	const LLUUID&		getLocalTextureID(LLAvatarAppearanceDefines::ETextureIndex type, U32 index) const;
 	void				setLocalTextureTE(U8 te, LLViewerTexture* image, U32 index);
-	/*virtual*/ void	setLocalTexture(LLAvatarAppearanceDefines::ETextureIndex type, LLViewerTexture* tex, BOOL baked_version_exits, U32 index);
+	/*virtual*/ void	setLocalTexture(LLAvatarAppearanceDefines::ETextureIndex type, LLViewerTexture* tex, BOOL baked_version_exits, U32 index) override;
 protected:
-	/*virtual*/ void	setBakedReady(LLAvatarAppearanceDefines::ETextureIndex type, BOOL baked_version_exists, U32 index);
+	/*virtual*/ void	setBakedReady(LLAvatarAppearanceDefines::ETextureIndex type, BOOL baked_version_exists, U32 index) override;
 	void				localTextureLoaded(BOOL succcess, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
 	void				getLocalTextureByteCount(S32* gl_byte_count) const;
-	/*virtual*/ void	addLocalTextureStats(LLAvatarAppearanceDefines::ETextureIndex i, LLViewerFetchedTexture* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked);
+	/*virtual*/ void	addLocalTextureStats(LLAvatarAppearanceDefines::ETextureIndex i, LLViewerFetchedTexture* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked) override;
 	LLLocalTextureObject* getLocalTextureObject(LLAvatarAppearanceDefines::ETextureIndex i, U32 index) const;
 
 private:
 	static void			onLocalTextureLoaded(BOOL succcess, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
 
-	/*virtual*/	void	setImage(const U8 te, LLViewerTexture *imagep, const U32 index); 
-	/*virtual*/ LLViewerTexture* getImage(const U8 te, const U32 index) const;
+	/*virtual*/	void	setImage(const U8 te, LLViewerTexture *imagep, const U32 index) override; 
+	/*virtual*/ LLViewerTexture* getImage(const U8 te, const U32 index) const override;
 
 
 	//--------------------------------------------------------------------
@@ -215,13 +217,21 @@ class LLVOAvatarSelf final :
 	LLAvatarAppearanceDefines::ETextureIndex getBakedTE(const LLViewerTexLayerSet* layerset ) const;
 	// SUNSHINE CLEANUP - dead? or update to just call request appearance update?
 	void				forceBakeAllTextures(bool slam_for_debug = false);
+
+	void				setNewBakedTexture(LLAvatarAppearanceDefines::EBakedTextureIndex i, const LLUUID &uuid);
+	void				setNewBakedTexture(LLAvatarAppearanceDefines::ETextureIndex i, const LLUUID& uuid);
+	void				setCachedBakedTexture(LLAvatarAppearanceDefines::ETextureIndex i, const LLUUID& uuid);
+	static void			processRebakeAvatarTextures(LLMessageSystem* msg, void**);
+
 protected:
-	/*virtual*/ void	removeMissingBakedTextures();
+	/*virtual*/ void	removeMissingBakedTextures() override;
 
 	//--------------------------------------------------------------------
 	// Layers
 	//--------------------------------------------------------------------
 public:
+	void 				requestLayerSetUploads();
+	void				requestLayerSetUpload(LLAvatarAppearanceDefines::EBakedTextureIndex i);
 	void				requestLayerSetUpdate(LLAvatarAppearanceDefines::ETextureIndex i);
 	LLViewerTexLayerSet* getLayerSet(LLAvatarAppearanceDefines::EBakedTextureIndex baked_index) const;
 	LLViewerTexLayerSet* getLayerSet(LLAvatarAppearanceDefines::ETextureIndex index) const;
@@ -231,11 +241,11 @@ class LLVOAvatarSelf final :
 	// Composites
 	//--------------------------------------------------------------------
 public:
-	/* virtual */ void	invalidateComposite(LLTexLayerSet* layerset);
-	/* virtual */ void	invalidateAll();
-	/* virtual */ void	setCompositeUpdatesEnabled(bool b); // only works for self
-	/* virtual */ void  setCompositeUpdatesEnabled(U32 index, bool b);
-	/* virtual */ bool 	isCompositeUpdateEnabled(U32 index);
+	/* virtual */ void	invalidateComposite(LLTexLayerSet* layerset, BOOL upload_result) override;
+	/* virtual */ void	invalidateAll() override;
+	/* virtual */ void	setCompositeUpdatesEnabled(bool b) override; // only works for self
+	/* virtual */ void  setCompositeUpdatesEnabled(U32 index, bool b) override;
+	/* virtual */ bool 	isCompositeUpdateEnabled(U32 index) override;
 	void				setupComposites();
 	void				updateComposites();
 
@@ -273,7 +283,7 @@ class LLVOAvatarSelf final :
  **/
 
 public:
-	void				wearableUpdated(LLWearableType::EType type);
+	void				wearableUpdated(LLWearableType::EType type, BOOL upload_result);
 protected:
 	U32 getNumWearables(LLAvatarAppearanceDefines::ETextureIndex i) const;
 
@@ -391,6 +401,7 @@ class LLVOAvatarSelf final :
 	const std::string		debugDumpLocalTextureDataInfo(const LLViewerTexLayerSet* 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
 	void					sendViewerAppearanceChangeMetrics(); // send data associated with completing a change.
+	void 					checkForUnsupportedServerBakeAppearance();
 private:
 	LLFrameTimer    		mDebugSelfLoadTimer;
 	F32						mDebugTimeWearablesLoaded;
-- 
GitLab