diff --git a/doc/contributions.txt b/doc/contributions.txt
index a693c22abcfe0a48bbf0c35e36f039b3890d6645..9368781c9a6cc27e81d9a2a3b4830da743a261eb 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -236,6 +236,7 @@ Ansariel Hiller
 	SL-15226
 	SL-15227
 	SL-15398
+	SL-18432
 Aralara Rajal
 Arare Chantilly
 	CHUIBUG-191
diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake
index 572422d080ee97c0b8da58b049a39d4066cb9bfc..d81d3ac1f08e477bdb0a8f31688bbc26db0cede1 100644
--- a/indra/cmake/00-Common.cmake
+++ b/indra/cmake/00-Common.cmake
@@ -232,3 +232,4 @@ else (USESYSTEMLIBS)
 endif (USESYSTEMLIBS)
 
 endif(NOT DEFINED ${CMAKE_CURRENT_LIST_FILE}_INCLUDED)
+
diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp
index 7640dd70bd14e07cb7548d48c26d21556ae70b4d..2803afde60a1cf384ed891c273e505b6382c5c1b 100644
--- a/indra/llprimitive/lltextureentry.cpp
+++ b/indra/llprimitive/lltextureentry.cpp
@@ -512,16 +512,19 @@ S32 LLTextureEntry::setBumpShiny(U8 bump_shiny)
 
 void LLTextureEntry::setGLTFMaterial(LLGLTFMaterial* material)
 { 
-    // assert on precondtion:
-    // whether or not mGLTFMaterial is null, any existing override should have been nulled out 
-    // before calling setGLTFMaterial
-    // NOTE: if you're hitting this assert, try to make sure calling code is using LLViewerObject::setRenderMaterialID
-    llassert(getGLTFMaterialOverride() == nullptr);
-
-    mGLTFMaterial = material;
-    if (mGLTFMaterial == nullptr)
+    if (material != getGLTFMaterial())
     {
-        setGLTFRenderMaterial(nullptr);
+        // assert on precondtion:
+        // whether or not mGLTFMaterial is null, any existing override should have been nulled out 
+        // before calling setGLTFMaterial
+        // NOTE: if you're hitting this assert, try to make sure calling code is using LLViewerObject::setRenderMaterialID
+        llassert(getGLTFMaterialOverride() == nullptr);
+
+        mGLTFMaterial = material;
+        if (mGLTFMaterial == nullptr)
+        {
+            setGLTFRenderMaterial(nullptr);
+        }
     }
 }
 
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index e20c0e5c3f66cfdc98e7ca3c12b8980ebcf911b4..91d98fe0bfe8bdb926d9e2a749d0d430e6e0c14a 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -48,8 +48,6 @@
 #include "llthreadsafequeue.h"
 #include "stringize.h"
 #include "llframetimer.h"
-#include "commoncontrol.h" // TODO: Remove after testing
-#include "llsd.h" // TODO: Remove after testing
 
 // System includes
 #include <commdlg.h>
@@ -420,9 +418,7 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
     // best guess at available video memory in MB
     std::atomic<U32> mAvailableVRAM;
 
-    bool mTryUseDXGIAdapter; // TODO: Remove after testing
     IDXGIAdapter3* mDXGIAdapter = nullptr;
-    bool mTryUseD3DDevice; // TODO: Remove after testing
     LPDIRECT3D9 mD3D = nullptr;
     LPDIRECT3DDEVICE9 mD3DDevice = nullptr;
 };
@@ -4580,14 +4576,6 @@ U32 LLWindowWin32::getAvailableVRAMMegabytes()
 inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread()
     : ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE)
 {
-    const LLSD skipDXGI{ LL::CommonControl::get("Global", "DisablePrimaryGraphicsMemoryAccounting") }; // TODO: Remove after testing
-    LL_WARNS() << "DisablePrimaryGraphicsMemoryAccounting: " << skipDXGI << ", as boolean: " << skipDXGI.asBoolean() << LL_ENDL;
-    mTryUseDXGIAdapter = !skipDXGI.asBoolean();
-    LL_WARNS() << "mTryUseDXGIAdapter: " << mTryUseDXGIAdapter << LL_ENDL;
-    const LLSD skipD3D{ LL::CommonControl::get("Global", "DisableSecondaryGraphicsMemoryAccounting") }; // TODO: Remove after testing
-    LL_WARNS() << "DisableSecondaryGraphicsMemoryAccounting: " << skipD3D << ", as boolean: " << skipD3D.asBoolean() << LL_ENDL;
-    mTryUseD3DDevice = !skipD3D.asBoolean();
-    LL_WARNS() << "mTryUseD3DDevice: " << mTryUseD3DDevice << LL_ENDL;
     ThreadPool::start();
 }
 
@@ -4704,7 +4692,7 @@ void debugEnumerateGraphicsAdapters()
 
 void LLWindowWin32::LLWindowWin32Thread::initDX()
 {
-    if (mDXGIAdapter == NULL && mTryUseDXGIAdapter)
+    if (mDXGIAdapter == NULL)
     {
         debugEnumerateGraphicsAdapters();
 
@@ -4738,7 +4726,7 @@ void LLWindowWin32::LLWindowWin32Thread::initDX()
 
 void LLWindowWin32::LLWindowWin32Thread::initD3D()
 {
-    if (mDXGIAdapter == NULL && mD3DDevice == NULL && mTryUseD3DDevice && mWindowHandle != 0)
+    if (mDXGIAdapter == NULL && mD3DDevice == NULL && mWindowHandle != 0)
     {
         mD3D = Direct3DCreate9(D3D_SDK_VERSION);
         
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 88365e6651c854df1af811a78763499cdccd97bd..3f9a91e38f9c24d4d94f16b23eb69e61826cc3af 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -3387,29 +3387,6 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
-    <!-- TODO: Remove settings keys DisablePrimaryGraphicsMemoryAccounting and DisableSecondaryGraphicsMemoryAccounting after testing, and code that references them -->
-    <key>DisablePrimaryGraphicsMemoryAccounting</key>
-    <map>
-      <key>Comment</key>
-      <string>Disable the first method used to detect GPU memory use</string>
-      <key>Persist</key>
-      <integer>1</integer>
-      <key>Type</key>
-      <string>Boolean</string>
-      <key>Value</key>
-      <integer>0</integer>
-    </map>
-    <key>DisableSecondaryGraphicsMemoryAccounting</key>
-    <map>
-      <key>Comment</key>
-      <string>Disable the second method used to detect GPU memory use, used as a fallback when the first method fails</string>
-      <key>Persist</key>
-      <integer>1</integer>
-      <key>Type</key>
-      <string>Boolean</string>
-      <key>Value</key>
-      <integer>0</integer>
-    </map>
     <key>DisableTextHyperlinkActions</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl b/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl
index 32f157e9d25b90e8b9c25c9c64e3b70b51ea11f2..839e10ce5e5ea8c284d7643f2271353588579589 100644
--- a/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl
+++ b/indra/newview/app_settings/shaders/class1/interface/radianceGenF.glsl
@@ -124,7 +124,7 @@ vec4 prefilterEnvMap(vec3 R)
 	vec3 V = R;
 	vec4 color = vec4(0.0);
 	float totalWeight = 0.0;
-	float envMapDim = 256.0;
+	float envMapDim = 128.0;
     int numSamples = 4;
     
     float numMips = 6.0;
diff --git a/indra/newview/app_settings/shaders/class1/interface/reflectionmipF.glsl b/indra/newview/app_settings/shaders/class1/interface/reflectionmipF.glsl
index 9dd97a80b2daa594b21797e5100051fbe03c5876..f0d579f85eff53bf14ae67d54d4b8561de55f800 100644
--- a/indra/newview/app_settings/shaders/class1/interface/reflectionmipF.glsl
+++ b/indra/newview/app_settings/shaders/class1/interface/reflectionmipF.glsl
@@ -90,7 +90,7 @@ void main()
 
     // convert linear depth to distance
     vec3 v;
-    v.xy = depth_tc / 512.0 * 2.0 - 1.0;
+    v.xy = depth_tc / 256.0 * 2.0 - 1.0;
     v.z = 1.0;
     v = normalize(v);
     dist /= v.z;
diff --git a/indra/newview/llgltfmateriallist.cpp b/indra/newview/llgltfmateriallist.cpp
index 3b4e9406bb0d26a707d85c7a426b5b30c71854a3..8210efae96d0e6ed4c150cc12e08dc548aecbe6b 100644
--- a/indra/newview/llgltfmateriallist.cpp
+++ b/indra/newview/llgltfmateriallist.cpp
@@ -174,7 +174,7 @@ void LLGLTFMaterialList::queueOverrideUpdate(const LLUUID& id, S32 side, LLGLTFM
 void LLGLTFMaterialList::applyQueuedOverrides(LLViewerObject* obj)
 {
     const LLUUID& id = obj->getID();
-    auto& iter = mQueuedOverrides.find(id);
+    auto iter = mQueuedOverrides.find(id);
 
     if (iter != mQueuedOverrides.end())
     {
diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp
index 5329f1061248fc891cbe0ccc1c66c3323629339f..3d13985f080b94b02595d47c6a752bd84390c6f9 100644
--- a/indra/newview/llinspectobject.cpp
+++ b/indra/newview/llinspectobject.cpp
@@ -116,6 +116,7 @@ class LLInspectObject : public LLInspect
 	viewer_media_t		mMediaImpl;
 	LLMediaEntry*       mMediaEntry;
 	LLSafeHandle<LLObjectSelection> mObjectSelection;
+    boost::signals2::connection mSelectionUpdateSlot;
 };
 
 LLInspectObject::LLInspectObject(const LLSD& sd)
@@ -141,6 +142,10 @@ LLInspectObject::LLInspectObject(const LLSD& sd)
 
 LLInspectObject::~LLInspectObject()
 {
+    if (mSelectionUpdateSlot.connected())
+    {
+        mSelectionUpdateSlot.disconnect();
+    }
 }
 
 /*virtual*/
@@ -175,9 +180,12 @@ BOOL LLInspectObject::postBuild(void)
 	getChild<LLUICtrl>("more_info_btn")->setCommitCallback(
 		boost::bind(&LLInspectObject::onClickMoreInfo, this));
 
-	// Watch for updates to selection properties off the network
-	LLSelectMgr::getInstance()->mUpdateSignal.connect(
-		boost::bind(&LLInspectObject::update, this) );
+    if (!mSelectionUpdateSlot.connected())
+    {
+        // Watch for updates to selection properties off the network
+        mSelectionUpdateSlot = LLSelectMgr::getInstance()->mUpdateSignal.connect(
+            boost::bind(&LLInspectObject::update, this));
+    }
 
 	return TRUE;
 }
diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp
index 5ae16db1f608038775214e21238131972b6f5b71..7cd9b9198fc8b74655424631644e310e0248107d 100644
--- a/indra/newview/llmaterialeditor.cpp
+++ b/indra/newview/llmaterialeditor.cpp
@@ -67,6 +67,24 @@ const std::string MATERIAL_EMISSIVE_DEFAULT_NAME = "Emissive";
 
 const LLUUID LIVE_MATERIAL_EDITOR_KEY("6cf97162-8b68-49eb-b627-79886c9fd17d");
 
+// Dirty flags
+static const U32 MATERIAL_BASE_COLOR_DIRTY = 0x1 << 0;
+static const U32 MATERIAL_BASE_TRANSPARENCY_DIRTY = 0x1 << 1;
+static const U32 MATERIAL_BASE_COLOR_TEX_DIRTY = 0x1 << 2;
+
+static const U32 MATERIAL_NORMAL_TEX_DIRTY = 0x1 << 3;
+
+static const U32 MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY = 0x1 << 4;
+static const U32 MATERIAL_METALLIC_ROUGHTNESS_METALNESS_DIRTY = 0x1 << 5;
+static const U32 MATERIAL_METALLIC_ROUGHTNESS_ROUGHNESS_DIRTY = 0x1 << 6;
+
+static const U32 MATERIAL_EMISIVE_COLOR_DIRTY = 0x1 << 7;
+static const U32 MATERIAL_EMISIVE_TEX_DIRTY = 0x1 << 8;
+
+static const U32 MATERIAL_DOUBLE_SIDED_DIRTY = 0x1 << 9;
+static const U32 MATERIAL_ALPHA_MODE_DIRTY = 0x1 << 10;
+static const U32 MATERIAL_ALPHA_CUTOFF_DIRTY = 0x1 << 11;
+
 LLFloaterComboOptions::LLFloaterComboOptions()
     : LLFloater(LLSD())
 {
@@ -199,7 +217,7 @@ class LLMaterialEditorCopiedCallback : public LLInventoryCallback
 // Default constructor
 LLMaterialEditor::LLMaterialEditor(const LLSD& key)
     : LLPreview(key)
-    , mHasUnsavedChanges(false)
+    , mUnsavedChanges(0)
     , mExpectedUploadCost(0)
     , mUploadingTexturesCount(0)
 {
@@ -251,33 +269,30 @@ BOOL LLMaterialEditor::postBuild()
     getChild<LLUICtrl>("emissive_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
     getChild<LLUICtrl>("normal_upload_fee")->setTextArg("[FEE]", llformat("%d", upload_cost));
 
-    boost::function<void(LLUICtrl*, void*)> changes_callback = [this](LLUICtrl * ctrl, void*)
+    boost::function<void(LLUICtrl*, void*)> changes_callback = [this](LLUICtrl * ctrl, void* userData)
     {
-        setHasUnsavedChanges(true);
+        const U32 *flag = (const U32*)userData;
+        markChangesUnsaved(*flag);
         // Apply changes to object live
         applyToSelection();
     };
  
-    childSetCommitCallback("double sided", changes_callback, NULL);
+    childSetCommitCallback("double sided", changes_callback, (void*)&MATERIAL_DOUBLE_SIDED_DIRTY);
 
     // BaseColor
-    childSetCommitCallback("base color", changes_callback, NULL);
-    childSetCommitCallback("transparency", changes_callback, NULL);
-    childSetCommitCallback("alpha mode", changes_callback, NULL);
-    childSetCommitCallback("alpha cutoff", changes_callback, NULL);
-
-    // Metallic-Roughness
-    childSetCommitCallback("metalness factor", changes_callback, NULL);
-    childSetCommitCallback("roughness factor", changes_callback, NULL);
+    childSetCommitCallback("base color", changes_callback, (void*)&MATERIAL_BASE_COLOR_DIRTY);
+    childSetCommitCallback("transparency", changes_callback, (void*)&MATERIAL_BASE_TRANSPARENCY_DIRTY);
+    childSetCommitCallback("alpha mode", changes_callback, (void*)&MATERIAL_ALPHA_MODE_DIRTY);
+    childSetCommitCallback("alpha cutoff", changes_callback, (void*)&MATERIAL_ALPHA_CUTOFF_DIRTY);
 
     // Metallic-Roughness
-    childSetCommitCallback("metalness factor", changes_callback, NULL);
-    childSetCommitCallback("roughness factor", changes_callback, NULL);
+    childSetCommitCallback("metalness factor", changes_callback, (void*)&MATERIAL_METALLIC_ROUGHTNESS_METALNESS_DIRTY);
+    childSetCommitCallback("roughness factor", changes_callback, (void*)&MATERIAL_METALLIC_ROUGHTNESS_ROUGHNESS_DIRTY);
 
     // Emissive
-    childSetCommitCallback("emissive color", changes_callback, NULL);
+    childSetCommitCallback("emissive color", changes_callback, (void*)&MATERIAL_EMISIVE_COLOR_DIRTY);
 
-    childSetVisible("unsaved_changes", mHasUnsavedChanges);
+    childSetVisible("unsaved_changes", mUnsavedChanges);
 
     getChild<LLUICtrl>("total_upload_fee")->setTextArg("[FEE]", llformat("%d", 0));
 
@@ -302,6 +317,11 @@ void LLMaterialEditor::onClickCloseBtn(bool app_quitting)
 
 void LLMaterialEditor::onClose(bool app_quitting)
 {
+    if (mSelectionUpdateSlot.connected())
+    {
+        mSelectionUpdateSlot.disconnect();
+    }
+
     LLPreview::onClose(app_quitting);
 }
 
@@ -314,6 +334,7 @@ void LLMaterialEditor::setBaseColorId(const LLUUID& id)
 {
     mBaseColorTextureCtrl->setValue(id);
     mBaseColorTextureCtrl->setDefaultImageAssetID(id);
+    mBaseColorTextureCtrl->setTentative(FALSE);
 }
 
 void LLMaterialEditor::setBaseColorUploadId(const LLUUID& id)
@@ -328,7 +349,7 @@ void LLMaterialEditor::setBaseColorUploadId(const LLUUID& id)
         // Only set if we will need to upload this texture
         mBaseColorTextureUploadId = id;
     }
-    setHasUnsavedChanges(true);
+    markChangesUnsaved(MATERIAL_BASE_COLOR_TEX_DIRTY);
 }
 
 LLColor4 LLMaterialEditor::getBaseColor()
@@ -389,6 +410,7 @@ void LLMaterialEditor::setMetallicRoughnessId(const LLUUID& id)
 {
     mMetallicTextureCtrl->setValue(id);
     mMetallicTextureCtrl->setDefaultImageAssetID(id);
+    mMetallicTextureCtrl->setTentative(FALSE);
 }
 
 void LLMaterialEditor::setMetallicRoughnessUploadId(const LLUUID& id)
@@ -400,7 +422,7 @@ void LLMaterialEditor::setMetallicRoughnessUploadId(const LLUUID& id)
         childSetValue("metallic_upload_fee", getString("upload_fee_string"));
         mMetallicTextureUploadId = id;
     }
-    setHasUnsavedChanges(true);
+    markChangesUnsaved(MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY);
 }
 
 F32 LLMaterialEditor::getMetalnessFactor()
@@ -432,6 +454,7 @@ void LLMaterialEditor::setEmissiveId(const LLUUID& id)
 {
     mEmissiveTextureCtrl->setValue(id);
     mEmissiveTextureCtrl->setDefaultImageAssetID(id);
+    mEmissiveTextureCtrl->setTentative(FALSE);
 }
 
 void LLMaterialEditor::setEmissiveUploadId(const LLUUID& id)
@@ -443,7 +466,7 @@ void LLMaterialEditor::setEmissiveUploadId(const LLUUID& id)
         childSetValue("emissive_upload_fee", getString("upload_fee_string"));
         mEmissiveTextureUploadId = id;
     }
-    setHasUnsavedChanges(true);
+    markChangesUnsaved(MATERIAL_EMISIVE_TEX_DIRTY);
 }
 
 LLColor4 LLMaterialEditor::getEmissiveColor()
@@ -465,6 +488,7 @@ void LLMaterialEditor::setNormalId(const LLUUID& id)
 {
     mNormalTextureCtrl->setValue(id);
     mNormalTextureCtrl->setDefaultImageAssetID(id);
+    mNormalTextureCtrl->setTentative(FALSE);
 }
 
 void LLMaterialEditor::setNormalUploadId(const LLUUID& id)
@@ -476,7 +500,7 @@ void LLMaterialEditor::setNormalUploadId(const LLUUID& id)
         childSetValue("normal_upload_fee", getString("upload_fee_string"));
         mNormalTextureUploadId = id;
     }
-    setHasUnsavedChanges(true);
+    markChangesUnsaved(MATERIAL_NORMAL_TEX_DIRTY);
 }
 
 bool LLMaterialEditor::getDoubleSided()
@@ -489,12 +513,22 @@ void LLMaterialEditor::setDoubleSided(bool double_sided)
     childSetValue("double sided", double_sided);
 }
 
-void LLMaterialEditor::setHasUnsavedChanges(bool value)
+void LLMaterialEditor::resetUnsavedChanges()
 {
-    mHasUnsavedChanges = value;
-    childSetVisible("unsaved_changes", value);
+    mUnsavedChanges = 0;
+    childSetVisible("unsaved_changes", false);
+    setCanSave(false);
+
+    mExpectedUploadCost = 0;
+    getChild<LLUICtrl>("total_upload_fee")->setTextArg("[FEE]", llformat("%d", mExpectedUploadCost));
+}
 
-    if (mHasUnsavedChanges)
+void LLMaterialEditor::markChangesUnsaved(U32 dirty_flag)
+{
+    mUnsavedChanges |= dirty_flag;
+    childSetVisible("unsaved_changes", mUnsavedChanges);
+
+    if (mUnsavedChanges)
     {
         const LLInventoryItem* item = getItem();
         if (item)
@@ -589,7 +623,7 @@ void LLMaterialEditor::onCommitBaseColorTexture(LLUICtrl * ctrl, const LLSD & da
         // the texture that is not in use
         childSetValue("base_color_upload_fee", getString("no_upload_fee_string"));
     }
-    setHasUnsavedChanges(true);
+    markChangesUnsaved(MATERIAL_BASE_COLOR_TEX_DIRTY);
     applyToSelection();
 }
 
@@ -604,7 +638,7 @@ void LLMaterialEditor::onCommitMetallicTexture(LLUICtrl * ctrl, const LLSD & dat
     {
         childSetValue("metallic_upload_fee", getString("no_upload_fee_string"));
     }
-    setHasUnsavedChanges(true);
+    markChangesUnsaved(MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY);
     applyToSelection();
 }
 
@@ -619,7 +653,7 @@ void LLMaterialEditor::onCommitEmissiveTexture(LLUICtrl * ctrl, const LLSD & dat
     {
         childSetValue("emissive_upload_fee", getString("no_upload_fee_string"));
     }
-    setHasUnsavedChanges(true);
+    markChangesUnsaved(MATERIAL_EMISIVE_TEX_DIRTY);
     applyToSelection();
 }
 
@@ -634,7 +668,7 @@ void LLMaterialEditor::onCommitNormalTexture(LLUICtrl * ctrl, const LLSD & data)
     {
         childSetValue("normal_upload_fee", getString("no_upload_fee_string"));
     }
-    setHasUnsavedChanges(true);
+    markChangesUnsaved(MATERIAL_NORMAL_TEX_DIRTY);
     applyToSelection();
 }
 
@@ -1218,7 +1252,7 @@ void LLMaterialEditor::onSaveAsMsgCallback(const LLSD& notification, const LLSD&
                 else
                 {
                     std::string buffer = getEncodedAsset();
-                    LLPointer<LLInventoryCallback> cb = new LLMaterialEditorCopiedCallback(buffer, getKey(), mHasUnsavedChanges);
+                    LLPointer<LLInventoryCallback> cb = new LLMaterialEditorCopiedCallback(buffer, getKey(), mUnsavedChanges);
                     copy_inventory_item(
                         gAgent.getID(),
                         item->getPermissions().getOwner(),
@@ -1246,7 +1280,7 @@ void LLMaterialEditor::onSaveAsMsgCallback(const LLSD& notification, const LLSD&
 
 void LLMaterialEditor::onClickCancel()
 {
-    if (mHasUnsavedChanges)
+    if (mUnsavedChanges)
     {
         LLNotificationsUtil::add("UsavedMaterialChanges", LLSD(), LLSD(), boost::bind(&LLMaterialEditor::onCancelMsgCallback, this, _1, _2));
     }
@@ -1463,42 +1497,61 @@ void LLMaterialEditor::loadMaterialFromFile(const std::string& filename, S32 ind
             );
     }
 }
+void LLMaterialEditor::onSelectionChanged()
+{
+    mUnsavedChanges = 0;
+    clearTextures();
+    setFromSelection();
+    saveLiveValues();
+}
+
+void LLMaterialEditor::saveLiveValues()
+{
+    // Collect ids to be able to revert overrides.
+    // TODO: monitor selection changes and resave on selection changes
+    mObjectOverridesSavedValues.clear();
+    struct g : public LLSelectedObjectFunctor
+    {
+        g(LLMaterialEditor* me) : mEditor(me) {}
+        virtual bool apply(LLViewerObject* objectp)
+        {
+            if (!objectp)
+            {
+                return false;
+            }
+
+            U32 local_id = objectp->getLocalID();
+            S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces());
+            for (U8 te = 0; te < num_tes; te++)
+            {
+                LLUUID mat_id = objectp->getRenderMaterialID(te);
+                mEditor->mObjectOverridesSavedValues[local_id].push_back(mat_id);
+            }
+            return true;
+        }
+        LLMaterialEditor* mEditor;
+    } savefunc(this);
+    LLSelectMgr::getInstance()->getSelection()->applyToObjects(&savefunc);
+}
 
 void LLMaterialEditor::loadLive()
 {
     LLMaterialEditor* me = (LLMaterialEditor*)LLFloaterReg::getInstance("material_editor", LLSD(LIVE_MATERIAL_EDITOR_KEY));
-    if (me->setFromSelection())
+    if (me)
     {
+        me->setFromSelection();
         me->mIsOverride = true;
         me->setTitle(me->getString("material_override_title"));
         me->childSetVisible("save", false);
         me->childSetVisible("save_as", false);
-        me->mObjectOverridesSavedValues.clear();
 
-        // Collect ids to be able to revert overrides.
-        // TODO: monitor selection changes and resave on selection changes
-        struct g : public LLSelectedObjectFunctor
+        // Set up for selection changes updates
+        if (!me->mSelectionUpdateSlot.connected())
         {
-            g(LLMaterialEditor* me) : mEditor(me) {}
-            virtual bool apply(LLViewerObject* objectp)
-            {
-                if (!objectp)
-                {
-                    return false;
-                }
-
-                U32 local_id = objectp->getLocalID();
-                S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces());
-                for (U8 te = 0; te < num_tes; te++)
-                {
-                    LLUUID mat_id = objectp->getRenderMaterialID(te);
-                    mEditor->mObjectOverridesSavedValues[local_id].push_back(mat_id);
-                }
-                return true;
-            }
-            LLMaterialEditor* mEditor;
-        } savefunc(me);
-        LLSelectMgr::getInstance()->getSelection()->applyToObjects(&savefunc);
+            me->mSelectionUpdateSlot = LLSelectMgr::instance().mUpdateSignal.connect(boost::bind(&LLMaterialEditor::onSelectionChanged, me));
+        }
+        // Collect ids to be able to revert overrides on cancel.
+        me->saveLiveValues();
 
         me->openFloater();
         me->setFocus(TRUE);
@@ -1527,7 +1580,6 @@ void LLMaterialEditor::loadFromGLTFMaterial(LLUUID &asset_id)
     LLMaterialEditor* me = (LLMaterialEditor*)LLFloaterReg::getInstance("material_editor");
     me->mMaterialName = LLTrans::getString("New Material");
     me->setTitle(me->mMaterialName);
-    me->setHasUnsavedChanges(true);
     me->setFromGLTFMaterial(gGLTFMaterialList.getMaterial(asset_id));
     me->openFloater();
     me->setFocus(TRUE);
@@ -1629,7 +1681,7 @@ void LLMaterialEditor::loadMaterial(const tinygltf::Model &model_in, const std::
 
     setFromGltfMetaData(filename_lc, model_in, index);
 
-    setHasUnsavedChanges(true);
+    markChangesUnsaved(U32_MAX);
 
     openFloater();
     setFocus(TRUE);
@@ -1967,8 +2019,8 @@ class LLRenderMaterialFunctor : public LLSelectedTEFunctor
 class LLRenderMaterialOverrideFunctor : public LLSelectedTEFunctor
 {
 public:
-    LLRenderMaterialOverrideFunctor(LLMaterialEditor * me, std::string const & url, LLUUID const & asset_id)
-    : mEditor(me), mCapUrl(url), mAssetID(asset_id)
+    LLRenderMaterialOverrideFunctor(LLMaterialEditor * me, std::string const & url)
+    : mEditor(me), mCapUrl(url)
     {
 
     }
@@ -1983,12 +2035,78 @@ class LLRenderMaterialOverrideFunctor : public LLSelectedTEFunctor
 
         if (objectp && objectp->permModify() && objectp->getVolume())
         {
-            LLPointer<LLGLTFMaterial> material = new LLGLTFMaterial();
-            
-            mEditor->getGLTFMaterial(material);
+            // Get material from object
+            // Selection can cover multiple objects, and live editor is
+            // supposed to overwrite changed values only
+            LLTextureEntry* tep = objectp->getTE(te);
+            LLPointer<LLGLTFMaterial> material = tep->getGLTFRenderMaterial();
+
+            if (material.isNull())
+            {
+                material = new LLGLTFMaterial();
+            }
+            else
+            {
+                material = new LLGLTFMaterial(*material);
+            }
+
+            // Override object's values with values from editor where appropriate
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_BASE_COLOR_DIRTY)
+            {
+                material->mBaseColor = mEditor->getBaseColor();
+            }
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_BASE_TRANSPARENCY_DIRTY)
+            {
+                material->mBaseColor.mV[3] = mEditor->getTransparency();
+            }
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_BASE_COLOR_TEX_DIRTY)
+            {
+                material->mBaseColorId = mEditor->getBaseColorId();
+            }
+
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_NORMAL_TEX_DIRTY)
+            {
+                material->mNormalId = mEditor->getNormalId();
+            }
+
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY)
+            {
+                material->mMetallicRoughnessId = mEditor->getMetallicRoughnessId();
+            }
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_METALLIC_ROUGHTNESS_METALNESS_DIRTY)
+            {
+                material->mMetallicFactor = mEditor->getMetalnessFactor();
+            }
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_METALLIC_ROUGHTNESS_ROUGHNESS_DIRTY)
+            {
+                material->mRoughnessFactor = mEditor->getRoughnessFactor();
+            }
+
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_EMISIVE_COLOR_DIRTY)
+            {
+                material->mEmissiveColor = mEditor->getEmissiveColor();
+            }
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_EMISIVE_TEX_DIRTY)
+            {
+                material->mEmissiveId = mEditor->getEmissiveId();
+            }
+
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_DOUBLE_SIDED_DIRTY)
+            {
+                material->mDoubleSided = mEditor->getDoubleSided();
+            }
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_ALPHA_MODE_DIRTY)
+            {
+                material->setAlphaMode(mEditor->getAlphaMode());
+            }
+            if (mEditor->getUnsavedChangesFlags() & MATERIAL_ALPHA_CUTOFF_DIRTY)
+            {
+                material->mAlphaCutoff = mEditor->getAlphaCutoff();
+            }
 
             std::string overrides_json = material->asJSON();
             
+            
             LLSD overrides = llsd::map(
                 "object_id", objectp->getID(),
                 "side", te,
@@ -2002,7 +2120,6 @@ class LLRenderMaterialOverrideFunctor : public LLSelectedTEFunctor
 private:
     LLMaterialEditor * mEditor;
     std::string mCapUrl;
-    LLUUID mAssetID;
 };
 
 void LLMaterialEditor::applyToSelection()
@@ -2021,7 +2138,7 @@ void LLMaterialEditor::applyToSelection()
     {
         LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection();
         // TODO figure out how to get the right asset id in cases where we don't have a good one
-        LLRenderMaterialOverrideFunctor override_func(this, url, mAssetID);
+        LLRenderMaterialOverrideFunctor override_func(this, url);
         selected_objects->applyToTEs(&override_func);
     }
     else
@@ -2081,21 +2198,40 @@ bool LLMaterialEditor::setFromSelection()
 {
     struct LLSelectedTEGetGLTFRenderMaterial : public LLSelectedTEGetFunctor<LLPointer<LLGLTFMaterial> >
     {
-        LLPointer<LLGLTFMaterial> get(LLViewerObject* object, S32 te_index)
+        LLPointer<LLGLTFMaterial> get(LLViewerObject* objectp, S32 te_index)
         {
-            return object->getTE(te_index)->getGLTFRenderMaterial(); // present user with combined override + asset
+            if (!objectp)
+            {
+                return nullptr;
+            }
+            LLTextureEntry *tep = objectp->getTE(te_index);
+            if (!tep)
+            {
+                return nullptr;
+            }
+            return tep->getGLTFRenderMaterial(); // present user with combined override + asset
         }
     } func;
 
     LLPointer<LLGLTFMaterial> mat;
-    LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&func, mat);
+    bool identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&func, mat);
     if (mat.notNull())
     {
         setFromGLTFMaterial(mat);
-        return true;
+    }
+    else
+    {
+        // pick defaults from a blank material;
+        LLGLTFMaterial blank_mat;
+        setFromGLTFMaterial(&blank_mat);
     }
 
-    return false;
+    mBaseColorTextureCtrl->setTentative(!identical);
+    mMetallicTextureCtrl->setTentative(!identical);
+    mEmissiveTextureCtrl->setTentative(!identical);
+    mNormalTextureCtrl->setTentative(!identical);
+
+    return mat.notNull();
 }
 
 
@@ -2134,7 +2270,7 @@ void LLMaterialEditor::loadAsset()
             {
                 mAssetStatus = PREVIEW_ASSET_LOADED;
                 loadDefaults();
-                setHasUnsavedChanges(false);
+                resetUnsavedChanges();
                 setEnableEditing(allow_modify && !source_library);
             }
             else
@@ -2159,7 +2295,7 @@ void LLMaterialEditor::loadAsset()
                         LL_WARNS() << "Can't find object " << mObjectUUID << " associated with notecard." << LL_ENDL;
                         mAssetID.setNull();
                         mAssetStatus = PREVIEW_ASSET_LOADED;
-                        setHasUnsavedChanges(false);
+                        resetUnsavedChanges();
                         setEnableEditing(allow_modify && !source_library);
                         return;
                     }
@@ -2246,7 +2382,7 @@ void LLMaterialEditor::onLoadComplete(const LLUUID& asset_uuid,
             BOOL allow_modify = editor->canModify(editor->mObjectUUID, editor->getItem());
             BOOL source_library = editor->mObjectUUID.isNull() && gInventory.isObjectDescendentOf(editor->mItemUUID, gInventory.getLibraryRootFolderID());
             editor->setEnableEditing(allow_modify && !source_library);
-            editor->setHasUnsavedChanges(false);
+            editor->resetUnsavedChanges();
             editor->mAssetStatus = PREVIEW_ASSET_LOADED;
             editor->setEnabled(true); // ready for use
         }
@@ -2424,6 +2560,16 @@ S32 LLMaterialEditor::saveTextures()
     }
 
     // discard upload buffers once textures have been saved
+    clearTextures();
+
+    // asset storage can callback immediately, causing a decrease
+    // of mUploadingTexturesCount, report amount of work scheduled
+    // not amount of work remaining
+    return work_count;
+}
+
+void LLMaterialEditor::clearTextures()
+{
     mBaseColorJ2C = nullptr;
     mNormalJ2C = nullptr;
     mEmissiveJ2C = nullptr;
@@ -2438,11 +2584,6 @@ S32 LLMaterialEditor::saveTextures()
     mNormalTextureUploadId.setNull();
     mMetallicTextureUploadId.setNull();
     mEmissiveTextureUploadId.setNull();
-
-    // asset storage can callback immediately, causing a decrease
-    // of mUploadingTexturesCount, report amount of work scheduled
-    // not amount of work remaining
-    return work_count;
 }
 
 void LLMaterialEditor::loadDefaults()
diff --git a/indra/newview/llmaterialeditor.h b/indra/newview/llmaterialeditor.h
index 53f19d877be0ddef2a6c8064aec537cdd671b3a0..091f51234f90208a184dd6cfa0bab5b37a498fdf 100644
--- a/indra/newview/llmaterialeditor.h
+++ b/indra/newview/llmaterialeditor.h
@@ -103,6 +103,8 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
     // will promt to select specific one
     static void loadMaterialFromFile(const std::string& filename, S32 index = -1);
 
+    void onSelectionChanged(); // // live overrides selection changes
+    void saveLiveValues(); // for restoration on cancel
     static void loadLive();
     static void loadObjectSave();
 
@@ -118,6 +120,7 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
     // save textures to inventory if needed
     // returns amount of scheduled uploads
     S32 saveTextures();
+    void clearTextures();
 
     void onClickSave();
 
@@ -206,7 +209,6 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
     bool getDoubleSided();
     void setDoubleSided(bool double_sided);
 
-    void setHasUnsavedChanges(bool value);
     void setCanSaveAs(bool value);
     void setCanSave(bool value);
     void setEnableEditing(bool can_modify);
@@ -219,6 +221,8 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
     // initialize the UI from a default GLTF material
     void loadDefaults();
 
+    U32 getUnsavedChangesFlags() { return mUnsavedChanges; }
+
 private:
     void setFromGLTFMaterial(LLGLTFMaterial* mat);
     bool setFromSelection();
@@ -266,7 +270,10 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
     // based on what we know about it.
     const std::string buildMaterialDescription();
 
-    bool mHasUnsavedChanges;
+    void resetUnsavedChanges();
+    void markChangesUnsaved(U32 dirty_flag);
+
+    U32 mUnsavedChanges; // flags to indicate individual changed parameters
     S32 mUploadingTexturesCount;
     S32 mExpectedUploadCost;
     std::string mMaterialNameShort;
@@ -277,5 +284,6 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener
     // local id, texture ids per face for object overrides
     // for "cancel" support
     std::map<U32, uuid_vec_t> mObjectOverridesSavedValues;
+    boost::signals2::connection mSelectionUpdateSlot;
 };
 
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
index ac508c4e8a79046e93370e160a04b5835c30fd3e..e4a67d79823e819e2d8ed176239f362babfdee48 100644
--- a/indra/newview/llselectmgr.cpp
+++ b/indra/newview/llselectmgr.cpp
@@ -1948,18 +1948,7 @@ void LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id)
                 objectp->setParameterEntryInUse(LLNetworkData::PARAMS_RENDER_MATERIAL, TRUE, false /*prevent an update*/);
             }
 
-            if (te != -1)
-            {
-                objectp->setRenderMaterialID(te, asset_id);
-            }
-            else // Shouldn't happen?
-            {
-                S32 num_faces = objectp->getNumTEs();
-                for (S32 face = 0; face < num_faces; face++)
-                {
-                    objectp->setRenderMaterialID(te, asset_id);
-                }
-            }
+            objectp->setRenderMaterialID(te, asset_id);
 
             return true;
         }
diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp
index 7fa06f51e30e51d5a84e23e6cb26d189390c2fcb..6216057c17c4d9a9a83d597b4e2b12c4346201a2 100644
--- a/indra/newview/llsidepaneltaskinfo.cpp
+++ b/indra/newview/llsidepaneltaskinfo.cpp
@@ -77,7 +77,7 @@ static LLPanelInjector<LLSidepanelTaskInfo> t_task_info("sidepanel_task_info");
 LLSidepanelTaskInfo::LLSidepanelTaskInfo()
 {
 	setMouseOpaque(FALSE);
-	LLSelectMgr::instance().mUpdateSignal.connect(boost::bind(&LLSidepanelTaskInfo::refreshAll, this));
+    mSelectionUpdateSlot = LLSelectMgr::instance().mUpdateSignal.connect(boost::bind(&LLSidepanelTaskInfo::refreshAll, this));
 }
 
 
@@ -85,6 +85,11 @@ LLSidepanelTaskInfo::~LLSidepanelTaskInfo()
 {
 	if (sActivePanel == this)
 		sActivePanel = NULL;
+
+    if (mSelectionUpdateSlot.connected())
+    {
+        mSelectionUpdateSlot.disconnect();
+    }
 }
 
 // virtual
diff --git a/indra/newview/llsidepaneltaskinfo.h b/indra/newview/llsidepaneltaskinfo.h
index dc259cb22df6d1ff7aba41588c22e16b491364c9..ac9c57f2e2252320736527f4fbd54e3a01ac7058 100644
--- a/indra/newview/llsidepaneltaskinfo.h
+++ b/indra/newview/llsidepaneltaskinfo.h
@@ -154,6 +154,8 @@ class LLSidepanelTaskInfo : public LLSidepanelInventorySubpanel
 	LLView*		mDAE;
 	LLView*		mDAN;
 	LLView*		mDAF;
+
+    boost::signals2::connection mSelectionUpdateSlot;
 };
 
 
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
index a0c24ca7dcd75c2c9660e8c47b48328e649c864b..0d16b818f120830795d6b39427d605a145d714a6 100644
--- a/indra/newview/llspatialpartition.h
+++ b/indra/newview/llspatialpartition.h
@@ -274,7 +274,7 @@ class LLSpatialGroup : public LLOcclusionCullingGroup
                 return lhs->mAvatarp < rhs->mAvatarp;
             }
 
-            return lhs->mRenderOrder < rhs->mRenderOrder;
+            return lhs->mRenderOrder > rhs->mRenderOrder;
         }
     };
 
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 18d215f9f4c38d80aa96318fde5af9b261c1939d..e1b92e5f2ed35963d64ca5634da0d6c4f7c8cb1a 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -2956,30 +2956,20 @@ void handle_object_edit()
 	return;
 }
 
-void load_life_gltf_material(bool copy)
+void handle_object_edit_gltf_material()
 {
-    update_camera();
-
-    LLSelectedTEGetmatIdAndPermissions func;
-    LLUUID mat_id;
-    LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&func, mat_id);
-
-    if (copy)
+    if (!LLFloaterReg::instanceVisible("build"))
     {
-        LLMaterialEditor::loadFromGLTFMaterial(mat_id);
+        handle_object_edit(); // does update_camera();
     }
     else
     {
-        LLMaterialEditor::loadLive();
-    }
+        update_camera();
 
-    LLViewerJoystick::getInstance()->moveObjects(true);
-    LLViewerJoystick::getInstance()->setNeedsReset(true);
-}
+        LLViewerJoystick::getInstance()->moveObjects(true);
+        LLViewerJoystick::getInstance()->setNeedsReset(true);
+    }
 
-void handle_object_edit_gltf_material()
-{
-    handle_object_edit();
     LLMaterialEditor::loadLive();
 }
 
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 7dbe0652b11a97d63787e11f8a0aad55a187d00a..cbdf3964e566e9477aeaaf46dcd778063383cdf2 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -5335,7 +5335,7 @@ S32 LLViewerObject::setTEGLTFMaterialOverride(U8 te, LLGLTFMaterial* override_ma
     tep->setGLTFMaterialOverride(override_mat);
 
     // if override mat exists, we must also have a source mat
-    llassert(override_mat ? src_mat : true);
+    llassert(override_mat ? bool(src_mat) : true);
 
     if (override_mat && src_mat)
     {
@@ -7113,40 +7113,55 @@ const LLUUID& LLViewerObject::getRenderMaterialID(U8 te) const
     return LLUUID::null;
 }
 
-void LLViewerObject::setRenderMaterialID(U8 te, const LLUUID& id, bool update_server)
+void LLViewerObject::setRenderMaterialID(S32 te_in, const LLUUID& id, bool update_server)
 {
-    if (update_server)
+    S32 start_idx = 0;
+    S32 end_idx = getNumTEs();
+
+    if (te_in != -1)
     {
-        // clear out any existing override data and render material
-        getTE(te)->setGLTFMaterialOverride(nullptr);
-        getTE(te)->setGLTFRenderMaterial(nullptr);
-
-        LLCoros::instance().launch("modifyMaterialCoro", 
-            std::bind(&LLGLTFMaterialList::modifyMaterialCoro, 
-                gAgent.getRegionCapability("ModifyMaterialParams"), 
-                llsd::map(
-                    "object_id", getID(),
-                    "side", te)));
+        start_idx = te_in;
+        end_idx = start_idx + 1;
     }
 
-    if (id.notNull())
+    start_idx = llmax(start_idx, 0);
+    end_idx = llmin(end_idx, (S32) getNumTEs());
+
+    for (S32 te = start_idx; te < end_idx; ++te)
     {
-        getTE(te)->setGLTFMaterial(gGLTFMaterialList.getMaterial(id));
+        if (update_server)
+        {
+            // clear out any existing override data and render material
+            getTE(te)->setGLTFMaterialOverride(nullptr);
+            getTE(te)->setGLTFRenderMaterial(nullptr);
+
+            LLCoros::instance().launch("modifyMaterialCoro",
+                std::bind(&LLGLTFMaterialList::modifyMaterialCoro,
+                    gAgent.getRegionCapability("ModifyMaterialParams"),
+                    llsd::map(
+                        "object_id", getID(),
+                        "side", te)));
+        }
 
-        if (!hasRenderMaterialParams())
+        if (id.notNull())
         {
-            // make sure param section exists
-            // but do not update server to avoid race conditions
-            ExtraParameter* param = getExtraParameterEntryCreate(LLNetworkData::PARAMS_RENDER_MATERIAL);
-            if (param)
+            getTE(te)->setGLTFMaterial(gGLTFMaterialList.getMaterial(id));
+
+            if (!hasRenderMaterialParams())
             {
-                param->in_use = true;
+                // make sure param section exists
+                // but do not update server to avoid race conditions
+                ExtraParameter* param = getExtraParameterEntryCreate(LLNetworkData::PARAMS_RENDER_MATERIAL);
+                if (param)
+                {
+                    param->in_use = true;
+                }
             }
         }
-    }
-    else
-    {
-        getTE(te)->setGLTFMaterial(nullptr);
+        else
+        {
+            getTE(te)->setGLTFMaterial(nullptr);
+        }
     }
 
     faceMappingChanged();
@@ -7155,82 +7170,35 @@ void LLViewerObject::setRenderMaterialID(U8 te, const LLUUID& id, bool update_se
     LLRenderMaterialParams* param_block = (LLRenderMaterialParams*)getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL);
     if (param_block)
     {
-        param_block->setMaterial(te, id);
+        for (S32 te = start_idx; te < end_idx; ++te)
+        {
+            param_block->setMaterial(te, id);
 
-        if (param_block->isEmpty())
-        { // might be empty if id is null
-            if (hasRenderMaterialParams())
-            {
-                if (update_server)
+            if (param_block->isEmpty())
+            { // might be empty if id is null
+                if (hasRenderMaterialParams())
                 {
-                    setParameterEntryInUse(LLNetworkData::PARAMS_RENDER_MATERIAL, FALSE, true);
-                }
-                else
-                {
-                    ExtraParameter* param = getExtraParameterEntryCreate(LLNetworkData::PARAMS_RENDER_MATERIAL);
-                    if (param)
+                    if (update_server)
                     {
-                        param->in_use = false;
+                        setParameterEntryInUse(LLNetworkData::PARAMS_RENDER_MATERIAL, FALSE, true);
+                    }
+                    else
+                    {
+                        ExtraParameter* param = getExtraParameterEntryCreate(LLNetworkData::PARAMS_RENDER_MATERIAL);
+                        if (param)
+                        {
+                            param->in_use = false;
+                        }
                     }
                 }
             }
         }
-        else if (update_server)
-        {
-            parameterChanged(LLNetworkData::PARAMS_RENDER_MATERIAL, true);
-        }
     }
 }
 
 void LLViewerObject::setRenderMaterialIDs(const LLUUID& id)
 {
-    if (id.notNull())
-    {
-        if (!hasRenderMaterialParams())
-        {
-            // make sure param section exists
-            // but do not update server to avoid race conditions
-            ExtraParameter* param = getExtraParameterEntryCreate(LLNetworkData::PARAMS_RENDER_MATERIAL);
-            if (param)
-            {
-                param->in_use = true;
-            }
-        }
-    }
-
-    LLRenderMaterialParams* param_block = nullptr;
-    if (hasRenderMaterialParams())
-    {
-        param_block = (LLRenderMaterialParams*)getParameterEntry(LLNetworkData::PARAMS_RENDER_MATERIAL);
-    }
-
-    LLGLTFMaterial* material = id.isNull() ? nullptr : gGLTFMaterialList.getMaterial(id);
-    const S32 num_tes = llmin((S32)getNumTEs(), (S32)getNumFaces());
-
-    for (S32 te = 0; te < num_tes; te++)
-    {
-        getTE(te)->setGLTFMaterial(material);
-
-        if (param_block)
-        {
-            param_block->setMaterial(te, id);
-        }
-    }
-
-    faceMappingChanged();
-    gPipeline.markTextured(mDrawable);
-
-    if (param_block)
-    {
-        if (param_block->isEmpty())
-        {
-            setHasRenderMaterialParams(false);
-        }
-        else
-        {
-            parameterChanged(LLNetworkData::PARAMS_RENDER_MATERIAL, true);
-        }
-    }
+    setRenderMaterialID(-1, id);
 }
 
 void LLViewerObject::setRenderMaterialIDs(const LLRenderMaterialParams* material_params, bool local_origin)
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index 31e82545ec53805bffa0311101790b161bfeffa3..d871382256feea79415b5f7707859d98364a02fc 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -184,7 +184,12 @@ class LLViewerObject
     void setHasRenderMaterialParams(bool has_params);
 
     const LLUUID& getRenderMaterialID(U8 te) const;
-    void setRenderMaterialID(U8 te, const LLUUID& id, bool update_server = true);
+
+    // set the RenderMaterialID for the given TextureEntry
+    // te - TextureEntry index to set, or -1 for all TEs
+    // id - asset id of material asset
+    // update_server - if true, will send updates to server
+    void setRenderMaterialID(S32 te, const LLUUID& id, bool update_server = true);
     void setRenderMaterialIDs(const LLUUID& id);
 
 	virtual BOOL	isHUDAttachment() const { return FALSE; }
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 8415d457df214731292f9ec04e341643c44e5dd6..663dd2d9ec02c3d49ed3983547fbf315892f44bb 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -5705,7 +5705,7 @@ static inline void add_face(T*** list, U32* count, T* face)
     {
         if (count[1] < MAX_FACE_COUNT)
         {
-            //face->setDrawOrderIndex(count[1]);
+            face->setDrawOrderIndex(count[1]);
             list[1][count[1]++] = face;
         }
     }
@@ -5713,36 +5713,12 @@ static inline void add_face(T*** list, U32* count, T* face)
     {
         if (count[0] < MAX_FACE_COUNT)
         {
-            //face->setDrawOrderIndex(count[0]);
+            face->setDrawOrderIndex(count[0]);
             list[0][count[0]++] = face;
         }
     }
 }
 
-// return index into linkset for given object (0 for root prim)
-U32 get_linkset_index(LLVOVolume* vobj)
-{
-    LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWABLE;
-    if (vobj->isRootEdit())
-    {
-        return 0;
-    }
-
-    LLViewerObject* root = vobj->getRootEdit();
-    U32 idx = 1;
-    for (const auto& child : root->getChildren())
-    {
-        if (child == vobj)
-        {
-            return idx;
-        }
-        ++idx;
-    }
-
-    llassert(false);
-    return idx; //should never get here
-}
-
 void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
@@ -5908,8 +5884,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
             {
                 avatar->addAttachmentOverridesForObject(vobj, NULL, false);
             }
-            
-            U32 linkset_index = get_linkset_index(vobj);
 
             // Standard rigged mesh attachments: 
 			bool rigged = !vobj->isAnimatedObject() && skinInfo && vobj->isAttachment();
@@ -5930,9 +5904,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
 					continue;
 				}
 
-                // order by linkset index first and face index second
-                facep->setDrawOrderIndex(linkset_index * 100 + i);
-                
                 // HACK -- brute force this check every time a drawable gets rebuilt
                 vobj->updateTEMaterialTextures(i);
 #if 0
@@ -5972,6 +5943,11 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
                     if (facep->isState(LLFace::RIGGED))
                     { 
                         //face is not rigged but used to be, remove from rigged face pool
+                        LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*)facep->getPool();
+                        if (pool)
+                        {
+                            pool->removeFace(facep);
+                        }
                         facep->clearState(LLFace::RIGGED);
                         facep->mAvatar = NULL;
                         facep->mSkinInfo = NULL;
@@ -6441,14 +6417,6 @@ struct CompareBatchBreakerRigged
     }
 };
 
-struct CompareDrawOrder
-{
-    bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
-    {
-        return lhs->getDrawOrderIndex() < rhs->getDrawOrderIndex();
-    }
-};
-
 U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace** faces, U32 face_count, BOOL distance_sort, BOOL batch_textures, BOOL rigged)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
@@ -6482,11 +6450,6 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace
                 //sort faces by things that break batches, including avatar and mesh id
                 std::sort(faces, faces + face_count, CompareBatchBreakerRigged());
             }
-            else
-            {
-                // preserve legacy draw order for rigged faces
-                std::sort(faces, faces + face_count, CompareDrawOrder());
-            }
         }
         else if (!distance_sort)
         {
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index b2ca84f846b75f44efd560aecc3369182ccee81d..b60b64ed1fbeff1fa90fa60fa69630b6b4ba96bc 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -397,7 +397,6 @@ void LLPipeline::connectRefreshCachedSettingsSafe(const std::string name)
 
 void LLPipeline::init()
 {
-    LL_WARNS() << "Begin pipeline initialization" << LL_ENDL; // TODO: Remove after testing
 	refreshCachedSettings();
 
     mRT = &mMainRT;
@@ -416,7 +415,6 @@ void LLPipeline::init()
 	mInitialized = true;
 	
 	stop_glerror();
-    LL_WARNS() << "No GL errors yet. Pipeline initialization will continue." << LL_ENDL; // TODO: Remove after testing
 
 	//create render pass pools
 	getPool(LLDrawPool::POOL_ALPHA_PRE_WATER);
@@ -479,9 +477,7 @@ void LLPipeline::init()
 	
 	// Enable features
 		
-    LL_WARNS() << "Shader initialization start" << LL_ENDL; // TODO: Remove after testing
 	LLViewerShaderMgr::instance()->setShaders();
-    LL_WARNS() << "Shader initialization end" << LL_ENDL; // TODO: Remove after testing
 
 	stop_glerror();
 
diff --git a/indra/newview/skins/default/xui/en/menu_attachment_self.xml b/indra/newview/skins/default/xui/en/menu_attachment_self.xml
index 3b91b9df7ab33c2e63e87050ca848f2682843504..630a1981dfa66fec8de7ef5856dfdd1b909e48d9 100644
--- a/indra/newview/skins/default/xui/en/menu_attachment_self.xml
+++ b/indra/newview/skins/default/xui/en/menu_attachment_self.xml
@@ -12,6 +12,22 @@
         <menu_item_call.on_enable
          function="EnableEdit" />
     </menu_item_call>
+    <menu_item_call
+     label="Edit PBR Material"
+     name="EditGLTFMaterial">
+        <menu_item_call.on_click
+         function="Object.EditGLTFMaterial" />
+        <menu_item_call.on_enable
+         function="Object.EnableEditGLTFMaterial"/>
+    </menu_item_call>
+    <menu_item_call
+     label="Save material to inventory"
+     name="SaveGLTFMaterial">
+        <menu_item_call.on_click
+         function="Object.SaveGLTFMaterial" />
+        <menu_item_call.on_enable
+         function="Object.EnableSaveGLTFMaterial"/>
+    </menu_item_call>
     <menu_item_call
      enabled="false"
      label="Detach item"