From 4e5dce794923736ff7c32d55f1e75361e89a9d31 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 18 Mar 2025 17:04:08 +0200
Subject: #3736 LLExperienceCache shutdown crash

---
 indra/llmessage/llexperiencecache.cpp | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'indra')

diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp
index b5d0c93376..83a070df32 100644
--- a/indra/llmessage/llexperiencecache.cpp
+++ b/indra/llmessage/llexperiencecache.cpp
@@ -94,6 +94,8 @@ LLExperienceCache::LLExperienceCache()
 
 LLExperienceCache::~LLExperienceCache()
 {
+    // can exit without cleanup()
+    sShutdown = true;
 }
 
 void LLExperienceCache::initSingleton()
-- 
cgit v1.2.3


From d3d0728bac31099785fea1bf87f11b7c29d2425d Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Tue, 18 Mar 2025 19:29:30 +0200
Subject: #3547 Further reduce ParcelInfoRequest calls

---
 indra/newview/llpanelprofilepicks.cpp   |  4 ++--
 indra/newview/llremoteparcelrequest.cpp | 19 ++++++++++++++++++-
 indra/newview/llremoteparcelrequest.h   |  2 ++
 3 files changed, 22 insertions(+), 3 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp
index 09b8011ce4..a87ef4f0f9 100644
--- a/indra/newview/llpanelprofilepicks.cpp
+++ b/indra/newview/llpanelprofilepicks.cpp
@@ -55,7 +55,7 @@
 static LLPanelInjector<LLPanelProfilePicks> t_panel_profile_picks("panel_profile_picks");
 static LLPanelInjector<LLPanelProfilePick> t_panel_profile_pick("panel_profile_pick");
 
-constexpr F32 REQUEST_TIMOUT = 60;
+constexpr F32 REQUEST_TIMEOUT = 60;
 constexpr F32 LOCATION_CACHE_TIMOUT = 900;
 
 class LLPickHandler : public LLCommandHandler
@@ -842,7 +842,7 @@ std::string LLPanelProfilePick::getLocationNotice()
 
 void LLPanelProfilePick::sendParcelInfoRequest()
 {
-    if (mParcelId != mRequestedId || mLastRequestTimer.getElapsedTimeF32() > REQUEST_TIMOUT)
+    if (mParcelId != mRequestedId || mLastRequestTimer.getElapsedTimeF32() > REQUEST_TIMEOUT)
     {
         if (mRequestedId.notNull())
         {
diff --git a/indra/newview/llremoteparcelrequest.cpp b/indra/newview/llremoteparcelrequest.cpp
index 7b80e8c27f..d0aa1af2f3 100644
--- a/indra/newview/llremoteparcelrequest.cpp
+++ b/indra/newview/llremoteparcelrequest.cpp
@@ -102,7 +102,15 @@ void LLRemoteParcelInfoProcessor::processParcelInfoReply(LLMessageSystem* msg, v
     msg->getS32     ("Data", "SalePrice", parcel_data.sale_price);
     msg->getS32     ("Data", "AuctionID", parcel_data.auction_id);
 
-    LLRemoteParcelInfoProcessor::observer_multimap_t & observers = LLRemoteParcelInfoProcessor::getInstance()->mObservers;
+    LLRemoteParcelInfoProcessor* inst = LLRemoteParcelInfoProcessor::getInstance();
+
+    requests_map_t::const_iterator found = inst->mPendingParcelRequests.find(parcel_data.parcel_id);
+    if (found != inst->mPendingParcelRequests.end())
+    {
+        inst->mPendingParcelRequests.erase(found);
+    }
+
+    LLRemoteParcelInfoProcessor::observer_multimap_t & observers = inst->mObservers;
 
     typedef std::vector<observer_multimap_t::iterator> deadlist_t;
     deadlist_t dead_iters;
@@ -151,6 +159,15 @@ void LLRemoteParcelInfoProcessor::processParcelInfoReply(LLMessageSystem* msg, v
 
 void LLRemoteParcelInfoProcessor::sendParcelInfoRequest(const LLUUID& parcel_id)
 {
+    constexpr F32 DUPPLICATE_TIMEOUT = 0.5f;
+    requests_map_t::const_iterator found = mPendingParcelRequests.find(parcel_id);
+    if (found != mPendingParcelRequests.end() && found->second.getElapsedTimeF32() < DUPPLICATE_TIMEOUT)
+    {
+        // recently requested
+        return;
+    }
+    mPendingParcelRequests[parcel_id].reset();
+
     LLMessageSystem *msg = gMessageSystem;
 
     msg->newMessage("ParcelInfoRequest");
diff --git a/indra/newview/llremoteparcelrequest.h b/indra/newview/llremoteparcelrequest.h
index 1420b4032e..7059e14bec 100644
--- a/indra/newview/llremoteparcelrequest.h
+++ b/indra/newview/llremoteparcelrequest.h
@@ -91,6 +91,8 @@ public:
 private:
     typedef std::multimap<LLUUID, LLHandle<LLRemoteParcelInfoObserver> > observer_multimap_t;
     observer_multimap_t mObservers;
+    typedef std::map<LLUUID, LLTimer> requests_map_t;
+    requests_map_t mPendingParcelRequests; // Dupplicate request avoidance
 
     void regionParcelInfoCoro(std::string url, LLUUID regionId, LLVector3 posRegion, LLVector3d posGlobal, LLHandle<LLRemoteParcelInfoObserver> observerHandle);
 };
-- 
cgit v1.2.3


From c99e3167ed8549bc13d7df03b1e12dc15b0a080f Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 13 Mar 2025 23:55:38 +0200
Subject: #3644 Adjust throttle based of how busy buffer is

---
 indra/llmessage/llpacketring.cpp   |  6 ++++++
 indra/llmessage/llpacketring.h     |  1 +
 indra/llmessage/message.h          |  4 +---
 indra/newview/llappviewer.cpp      |  1 +
 indra/newview/llviewerthrottle.cpp | 14 +++++++++++---
 indra/newview/llviewerthrottle.h   |  3 +++
 6 files changed, 23 insertions(+), 6 deletions(-)

(limited to 'indra')

diff --git a/indra/llmessage/llpacketring.cpp b/indra/llmessage/llpacketring.cpp
index da3c502e9d..eb6650c6c5 100644
--- a/indra/llmessage/llpacketring.cpp
+++ b/indra/llmessage/llpacketring.cpp
@@ -344,6 +344,12 @@ bool LLPacketRing::expandRing()
     return true;
 }
 
+F32 LLPacketRing::getBufferLoadRate() const
+{
+    // goes up to MAX_BUFFER_RING_SIZE
+    return (F32)mNumBufferedPackets / (F32)DEFAULT_BUFFER_RING_SIZE;
+}
+
 void LLPacketRing::dumpPacketRingStats()
 {
     mNumDroppedPacketsTotal += mNumDroppedPackets;
diff --git a/indra/llmessage/llpacketring.h b/indra/llmessage/llpacketring.h
index 237efc12e0..572dcbd271 100644
--- a/indra/llmessage/llpacketring.h
+++ b/indra/llmessage/llpacketring.h
@@ -64,6 +64,7 @@ public:
     S32 getNumBufferedBytes() const { return mNumBufferedBytes; }
     S32 getNumDroppedPackets() const { return mNumDroppedPacketsTotal + mNumDroppedPackets; }
 
+    F32 getBufferLoadRate() const; // from 0 to 4 (0 - empty, 1 - default size is full)
     void dumpPacketRingStats();
 protected:
     // returns 'true' if we should intentionally drop a packet
diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h
index 1844d5e7cd..30945cac51 100644
--- a/indra/llmessage/message.h
+++ b/indra/llmessage/message.h
@@ -538,7 +538,6 @@ public:
 
     //void  buildMessage();
 
-    S32     zeroCode(U8 **data, S32 *data_size);
     S32     zeroCodeExpand(U8 **data, S32 *data_size);
     S32     zeroCodeAdjustCurrentSendTotal();
 
@@ -755,6 +754,7 @@ public:
     S32     getReceiveBytes() const;
 
     S32     getUnackedListSize() const          { return mUnackedListSize; }
+    F32     getBufferLoadRate() const           { return mPacketRing.getBufferLoadRate(); }
 
     //const char* getCurrentSMessageName() const { return mCurrentSMessageName; }
     //const char* getCurrentSBlockName() const { return mCurrentSBlockName; }
@@ -842,12 +842,10 @@ private:
     LLUUID mSessionID;
 
     void    addTemplate(LLMessageTemplate *templatep);
-    bool        decodeTemplate( const U8* buffer, S32 buffer_size, LLMessageTemplate** msg_template );
 
     void        logMsgFromInvalidCircuit( const LLHost& sender, bool recv_reliable );
     void        logTrustedMsgFromUntrustedCircuit( const LLHost& sender );
     void        logValidMsg(LLCircuitData *cdp, const LLHost& sender, bool recv_reliable, bool recv_resent, bool recv_acks );
-    void        logRanOffEndOfPacket( const LLHost& sender );
 
     class LLMessageCountInfo
     {
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index a05c2376a0..4cf651de33 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -5443,6 +5443,7 @@ void LLAppViewer::idleNetwork()
     // Retransmit unacknowledged packets.
     gXferManager->retransmitUnackedPackets();
     gAssetStorage->checkForTimeouts();
+    gViewerThrottle.setBufferLoadRate(gMessageSystem->getBufferLoadRate());
     gViewerThrottle.updateDynamicThrottle();
 
     // Check that the circuit between the viewer and the agent's current
diff --git a/indra/newview/llviewerthrottle.cpp b/indra/newview/llviewerthrottle.cpp
index b0a00c29a4..8d935e4243 100644
--- a/indra/newview/llviewerthrottle.cpp
+++ b/indra/newview/llviewerthrottle.cpp
@@ -48,6 +48,8 @@ const F32 MIN_FRACTIONAL = 0.2f;
 const F32 MIN_BANDWIDTH = 50.f;
 const F32 MAX_BANDWIDTH = 6000.f;
 const F32 STEP_FRACTIONAL = 0.1f;
+const F32 HIGH_BUFFER_LOAD_TRESHOLD = 1.f;
+const F32 LOW_BUFFER_LOAD_TRESHOLD = 0.8f;
 const LLUnit<F32, LLUnits::Percent> TIGHTEN_THROTTLE_THRESHOLD(3.0f); // packet loss % per s
 const LLUnit<F32, LLUnits::Percent> EASE_THROTTLE_THRESHOLD(0.5f); // packet loss % per s
 const F32 DYNAMIC_UPDATE_DURATION = 5.0f; // seconds
@@ -146,7 +148,7 @@ LLViewerThrottleGroup LLViewerThrottleGroup::operator-(const LLViewerThrottleGro
 
 void LLViewerThrottleGroup::sendToSim() const
 {
-    LL_INFOS() << "Sending throttle settings, total BW " << mThrottleTotal << LL_ENDL;
+    LL_DEBUGS("Throttle") << "Sending throttle settings, total BW " << mThrottleTotal << LL_ENDL;
     LLMessageSystem* msg = gMessageSystem;
 
     msg->newMessageFast(_PREHASH_AgentThrottle);
@@ -305,7 +307,10 @@ void LLViewerThrottle::updateDynamicThrottle()
     mUpdateTimer.reset();
 
     LLUnit<F32, LLUnits::Percent> mean_packets_lost = LLViewerStats::instance().getRecording().getMean(LLStatViewer::PACKETS_LOST_PERCENT);
-    if (mean_packets_lost > TIGHTEN_THROTTLE_THRESHOLD)
+    if (
+        mean_packets_lost > TIGHTEN_THROTTLE_THRESHOLD // already losing packets
+        || mBufferLoadRate >= HIGH_BUFFER_LOAD_TRESHOLD // let viewer sort through the backlog before it starts dropping packets
+        )
     {
         if (mThrottleFrac <= MIN_FRACTIONAL || mCurrentBandwidth / 1024.0f <= MIN_BANDWIDTH)
         {
@@ -318,7 +323,8 @@ void LLViewerThrottle::updateDynamicThrottle()
         mCurrent.sendToSim();
         LL_INFOS() << "Tightening network throttle to " << mCurrentBandwidth << LL_ENDL;
     }
-    else if (mean_packets_lost <= EASE_THROTTLE_THRESHOLD)
+    else if (mean_packets_lost <= EASE_THROTTLE_THRESHOLD
+             && mBufferLoadRate < LOW_BUFFER_LOAD_TRESHOLD)
     {
         if (mThrottleFrac >= MAX_FRACTIONAL || mCurrentBandwidth / 1024.0f >= MAX_BANDWIDTH)
         {
@@ -331,4 +337,6 @@ void LLViewerThrottle::updateDynamicThrottle()
         mCurrent.sendToSim();
         LL_INFOS() << "Easing network throttle to " << mCurrentBandwidth << LL_ENDL;
     }
+
+    mBufferLoadRate = 0;
 }
diff --git a/indra/newview/llviewerthrottle.h b/indra/newview/llviewerthrottle.h
index 28a24d04fc..9973c88549 100644
--- a/indra/newview/llviewerthrottle.h
+++ b/indra/newview/llviewerthrottle.h
@@ -70,12 +70,15 @@ public:
     void updateDynamicThrottle();
     void resetDynamicThrottle();
 
+    void setBufferLoadRate(F32 rate) { mBufferLoadRate = llmax(mBufferLoadRate, rate); }
+
     LLViewerThrottleGroup getThrottleGroup(const F32 bandwidth_kbps);
 
     static const std::string sNames[TC_EOF];
 protected:
     F32 mMaxBandwidth;
     F32 mCurrentBandwidth;
+    F32 mBufferLoadRate = 0;
 
     LLViewerThrottleGroup mCurrent;
 
-- 
cgit v1.2.3


From f04992676d5ab17cc34b310e06ce7d838c5232b9 Mon Sep 17 00:00:00 2001
From: Maxim Nikolenko <maximnproductengine@lindenlab.com>
Date: Wed, 19 Mar 2025 00:50:18 +0200
Subject: #3685 add test floater for new slapps

---
 indra/newview/CMakeLists.txt                       |   2 +
 indra/newview/app_settings/settings.xml            |  11 ++
 indra/newview/llfloaterslapptest.cpp               |  51 +++++++++
 indra/newview/llfloaterslapptest.h                 |  42 +++++++
 indra/newview/llinventorybridge.cpp                |  15 +++
 indra/newview/llviewerfloaterreg.cpp               |   5 +-
 .../skins/default/xui/en/floater_test_slapp.xml    | 123 +++++++++++++++++++++
 .../skins/default/xui/en/menu_inventory.xml        |   8 ++
 indra/newview/skins/default/xui/en/menu_viewer.xml |   7 ++
 9 files changed, 263 insertions(+), 1 deletion(-)
 create mode 100644 indra/newview/llfloaterslapptest.cpp
 create mode 100644 indra/newview/llfloaterslapptest.h
 create mode 100644 indra/newview/skins/default/xui/en/floater_test_slapp.xml

(limited to 'indra')

diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 3210b76649..ed29911a43 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -291,6 +291,7 @@ set(viewer_SOURCE_FILES
     llfloatersettingscolor.cpp
     llfloatersettingsdebug.cpp
     llfloatersidepanelcontainer.cpp
+    llfloaterslapptest.cpp
     llfloatersnapshot.cpp
     llfloatersounddevices.cpp
     llfloaterspellchecksettings.cpp
@@ -962,6 +963,7 @@ set(viewer_HEADER_FILES
     llfloatersettingscolor.h
     llfloatersettingsdebug.h
     llfloatersidepanelcontainer.h
+    llfloaterslapptest.h
     llfloatersnapshot.h
     llfloatersounddevices.h
     llfloaterspellchecksettings.h
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 0c83355a81..4ce7e0f729 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -3676,6 +3676,17 @@
         <key>Value</key>
         <integer>5000</integer>
     </map>
+    <key>InventoryExposeFolderID</key>
+    <map>
+        <key>Comment</key>
+        <string>Allows copying folder id from context menu</string>
+        <key>Persist</key>
+        <integer>0</integer>
+        <key>Type</key>
+        <string>Boolean</string>
+        <key>Value</key>
+        <integer>0</integer>
+    </map>
     <key>MarketplaceListingsSortOrder</key>
     <map>
       <key>Comment</key>
diff --git a/indra/newview/llfloaterslapptest.cpp b/indra/newview/llfloaterslapptest.cpp
new file mode 100644
index 0000000000..0075379529
--- /dev/null
+++ b/indra/newview/llfloaterslapptest.cpp
@@ -0,0 +1,51 @@
+/**
+ * @file llfloaterslapptest.cpp
+ *
+ * $LicenseInfo:firstyear=2025&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2025, 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 "llfloaterslapptest.h"
+#include "lluictrlfactory.h"
+
+#include "lllineeditor.h"
+#include "lltextbox.h"
+
+LLFloaterSLappTest::LLFloaterSLappTest(const LLSD& key)
+    :   LLFloater("floater_test_slapp")
+{
+}
+
+LLFloaterSLappTest::~LLFloaterSLappTest()
+{}
+
+bool LLFloaterSLappTest::postBuild()
+{
+    getChild<LLLineEditor>("remove_folder_id")->setKeystrokeCallback([this](LLLineEditor* editor, void*)
+        {
+            std::string slapp(getString("remove_folder_slapp"));
+            getChild<LLTextBox>("remove_folder_txt")->setValue(slapp + editor->getValue().asString());
+        }, NULL);
+
+    return true;
+}
diff --git a/indra/newview/llfloaterslapptest.h b/indra/newview/llfloaterslapptest.h
new file mode 100644
index 0000000000..ec727cb629
--- /dev/null
+++ b/indra/newview/llfloaterslapptest.h
@@ -0,0 +1,42 @@
+/**
+ * @file llfloaterslapptest.h
+ *
+ * $LicenseInfo:firstyear=2025&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2025, 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_LLFLOATERSLAPPTEST_H
+#define LL_LLFLOATERSLAPPTEST_H
+
+#include "llfloater.h"
+
+class LLFloaterSLappTest:
+    public LLFloater
+{
+    friend class LLFloaterReg;
+    virtual bool postBuild() override;
+
+private:
+    LLFloaterSLappTest(const LLSD& key);
+    ~LLFloaterSLappTest();
+};
+
+#endif
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 479b7c19ac..ce91f9a1f3 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -3599,6 +3599,13 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action)
         const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS);
         move_folder_to_marketplacelistings(cat, marketplacelistings_id, ("move_to_marketplace_listings" != action), (("copy_or_move_to_marketplace_listings" == action)));
     }
+    else if ("copy_folder_uuid" == action)
+    {
+        LLInventoryCategory* cat = gInventory.getCategory(mUUID);
+        if (!cat) return;
+        gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(mUUID.asString()));
+        return;
+    }
 }
 
 void LLFolderBridge::gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level)
@@ -4466,6 +4473,14 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t&   items
     {
         disabled_items.push_back(std::string("Delete System Folder"));
     }
+    else
+    {
+        static LLCachedControl<bool> show_copy_id(gSavedSettings, "InventoryExposeFolderID", false);
+        if (show_copy_id())
+        {
+            items.push_back(std::string("Copy UUID"));
+        }
+    }
 
     if (isAgentInventory() && !isMarketplaceListingsFolder())
     {
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index ef68609182..95c2a77ba8 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -137,6 +137,7 @@
 #include "llfloatersettingscolor.h"
 #include "llfloatersettingsdebug.h"
 #include "llfloatersidepanelcontainer.h"
+#include "llfloaterslapptest.h"
 #include "llfloatersnapshot.h"
 #include "llfloatersounddevices.h"
 #include "llfloaterspellchecksettings.h"
@@ -278,7 +279,8 @@ public:
                 "upload_model",
                 "upload_script",
                 "upload_sound",
-                "bulk_upload"
+                "bulk_upload",
+                "slapp_test"
             };
             return std::find(blacklist_untrusted.begin(), blacklist_untrusted.end(), fl_name) == blacklist_untrusted.end();
         }
@@ -499,6 +501,7 @@ void LLViewerFloaterReg::registerFloaters()
     LLFloaterReg::add("search", "floater_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSearch>);
     LLFloaterReg::add("profile", "floater_profile.xml",(LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterProfile>);
     LLFloaterReg::add("guidebook", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHowTo>);
+    LLFloaterReg::add("slapp_test", "floater_test_slapp.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSLappTest>);
 
     LLFloaterReg::add("big_preview", "floater_big_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterBigPreview>);
 
diff --git a/indra/newview/skins/default/xui/en/floater_test_slapp.xml b/indra/newview/skins/default/xui/en/floater_test_slapp.xml
new file mode 100644
index 0000000000..dd2720816a
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_test_slapp.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<floater
+ legacy_header_height="18"
+ height="300"
+ layout="topleft"
+ name="slapp_test"
+ title="SLAPP TEST"
+ width="500">
+   <floater.string
+    name="remove_folder_slapp">
+    secondlife://app/remove_folder/?folder_id=
+   </floater.string>
+   <text
+    type="string"
+    length="1"
+    top="20"
+    follows="left|top|right"
+    height="16"
+    name="trusted_txt"
+    font="SansSerifMedium"
+    text_color="white"
+    layout="topleft"
+    left="16">
+        Trusted source
+   </text>
+   <text
+    type="string"
+    length="1"
+    top_pad="8"
+    follows="left|top|right"
+    height="16"
+    layout="topleft"
+    left="16">
+    /wear_folder
+   </text>
+   <text
+    type="string"
+    length="1"
+    top_pad="5"
+    follows="left|top|right"
+    height="16"
+    width="450"
+    layout="topleft"
+    left="16">
+        secondlife://app/wear_folder/?folder_name=Daisy
+   </text>
+   <text
+    type="string"
+    length="1"
+    top_pad="8  "
+    follows="left|top|right"
+    height="16"
+    layout="topleft"
+    left="16">
+    /add_folder
+   </text>
+   <text
+    type="string"
+    length="1"
+    top_pad="5"
+    follows="left|top|right"
+    height="16"
+    width="450"
+    layout="topleft"
+    left="16">
+        secondlife://app/add_folder/?folder_name=Cardboard%20Boxbot
+   </text>
+   <text
+    type="string"
+    length="1"
+    top_pad="5"
+    follows="left|top|right"
+    height="16"
+    width="450"
+    layout="topleft">
+        secondlife://app/add_folder/?folder_id=59219db2-c260-87d3-213d-bb3bc298a3d8
+   </text>
+   <text
+    type="string"
+    length="1"
+    top_pad="8  "
+    follows="left|top|right"
+    height="16"
+    layout="topleft"
+    left="16">
+    /remove_folder
+   </text>
+   <text
+    type="string"
+    length="1"
+    top_pad="5"
+    follows="left|top|right"
+    height="16"
+    layout="topleft"
+    left="16">
+    Folder ID:
+   </text>
+   <line_editor
+    border_style="line"
+    border_thickness="1"
+    follows="left|top"
+    font="SansSerif"
+    height="18"
+    layout="topleft"
+    left="75"
+    max_length_bytes="50"
+    prevalidate_callback="ascii"
+    name="remove_folder_id"
+    top_delta="-2"
+    width="270" />
+   <text
+    type="string"
+    length="1"
+    top_pad="5"
+    follows="left|top|right"
+    height="16"
+    width="450"
+    name="remove_folder_txt"
+    layout="topleft"
+    left="16">
+        secondlife://app/remove_folder/?folder_id=
+   </text>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml
index d8aac71dd5..e57e3b0750 100644
--- a/indra/newview/skins/default/xui/en/menu_inventory.xml
+++ b/indra/newview/skins/default/xui/en/menu_inventory.xml
@@ -370,6 +370,14 @@
          function="Inventory.DoToSelected"
          parameter="copy_uuid" />
     </menu_item_call>
+    <menu_item_call
+     label="Copy UUID"
+     layout="topleft"
+     name="Copy UUID">
+        <menu_item_call.on_click
+         function="Inventory.DoToSelected"
+         parameter="copy_folder_uuid" />
+    </menu_item_call>
     <menu_item_call
      label="Show in Main Panel"
      layout="topleft"
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 6fb92f7b99..343f0c0059 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -3889,6 +3889,13 @@ function="World.EnvPreset"
                  function="Floater.Show"
                  parameter="font_test" />
             </menu_item_call>
+            <menu_item_call
+             label="Show SLapps Test"
+             name="Show SLapps Test">
+                <menu_item_call.on_click
+                 function="Floater.Show"
+                 parameter="slapp_test" />
+            </menu_item_call>
             <menu_item_check
              label="Show XUI Names"
              name="Show XUI Names">
-- 
cgit v1.2.3


From e92e79038fdb00318e65452f12ba000646dd22a2 Mon Sep 17 00:00:00 2001
From: nerodevo <100370411+nerodevo@users.noreply.github.com>
Date: Mon, 22 Apr 2024 15:21:38 -0500
Subject: Remove invalid text color parameter from panel_login_first.xml
 Follow-up to previous commit 4a07fd3. This will resolve the warning showing
 up when loading the viewer for the first time.

---
 indra/newview/skins/default/xui/en/panel_login_first.xml | 1 -
 1 file changed, 1 deletion(-)

(limited to 'indra')

diff --git a/indra/newview/skins/default/xui/en/panel_login_first.xml b/indra/newview/skins/default/xui/en/panel_login_first.xml
index c906e2f96c..d6ac71db94 100644
--- a/indra/newview/skins/default/xui/en/panel_login_first.xml
+++ b/indra/newview/skins/default/xui/en/panel_login_first.xml
@@ -179,7 +179,6 @@
             control_name="RememberPassword"
             follows="left|top"
             font="SansSerifLarge"
-            text_color="EmphasisColor"
             height="24"
             left="262"
             bottom_delta="0"
-- 
cgit v1.2.3


From d8f0faf128a12ff6c7406df966521471c219eeac Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 27 Mar 2024 00:28:04 +0200
Subject: viewer-private#131 landmark panel closes after a rename

if destination was changed
---
 indra/newview/llfloatercreatelandmark.cpp | 4 ++--
 indra/newview/llfloatercreatelandmark.h   | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llfloatercreatelandmark.cpp b/indra/newview/llfloatercreatelandmark.cpp
index 2ce8a7a212..864a1f0767 100644
--- a/indra/newview/llfloatercreatelandmark.cpp
+++ b/indra/newview/llfloatercreatelandmark.cpp
@@ -389,6 +389,7 @@ void LLFloaterCreateLandmark::setItem(const uuid_set_t& items)
             {
                 mItem = item;
                 mAssetID = mItem->getAssetUUID();
+                mParentID = mItem->getParentUUID();
                 setVisibleAndFrontmost(true);
                 break;
             }
@@ -418,8 +419,7 @@ void LLFloaterCreateLandmark::updateItem(const uuid_set_t& items, U32 mask)
                 closeFloater();
             }
 
-            LLUUID folder_id = mFolderCombo->getValue().asUUID();
-            if (folder_id != mItem->getParentUUID())
+            if (mParentID != mItem->getParentUUID())
             {
                 // user moved landmark in inventory,
                 // assume that we are done all other changes should already be commited
diff --git a/indra/newview/llfloatercreatelandmark.h b/indra/newview/llfloatercreatelandmark.h
index fa6d001b8e..f97ade2c1b 100644
--- a/indra/newview/llfloatercreatelandmark.h
+++ b/indra/newview/llfloatercreatelandmark.h
@@ -69,6 +69,7 @@ private:
     LLTextEditor*   mNotesEditor;
     LLUUID          mLandmarksID;
     LLUUID          mAssetID;
+    LLUUID          mParentID;
 
     LLLandmarksInventoryObserver*   mInventoryObserver;
     LLPointer<LLInventoryItem>      mItem;
-- 
cgit v1.2.3


From a5f1cca07589a53c659c9bea296ff6cae57b36df Mon Sep 17 00:00:00 2001
From: Alexander Gavriliuk <alexandrgproductengine@lindenlab.com>
Date: Tue, 18 Jun 2024 11:46:59 +0200
Subject: #1644 On-water property lines persist at water level

---
 indra/newview/llviewerparceloverlay.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llviewerparceloverlay.cpp b/indra/newview/llviewerparceloverlay.cpp
index 2e9b5de72b..19066217b1 100755
--- a/indra/newview/llviewerparceloverlay.cpp
+++ b/indra/newview/llviewerparceloverlay.cpp
@@ -581,7 +581,7 @@ void LLViewerParcelOverlay::addPropertyLine(F32 start_x, F32 start_y, F32 dx, F3
     outside_y += dy * (dy - LINE_WIDTH);
 
     // Middle part, full width
-    const S32 GRID_STEP = S32( PARCEL_GRID_STEP_METERS );
+    const S32 GRID_STEP = (S32)PARCEL_GRID_STEP_METERS;
     for (S32 i = 1; i < GRID_STEP; i++)
     {
         inside_z = land.resolveHeightRegion( inside_x, inside_y );
@@ -666,7 +666,9 @@ void LLViewerParcelOverlay::renderPropertyLines()
         return;
 
     LLSurface& land = mRegion->getLand();
-    F32 water_z = land.getWaterHeight() + 0.01f;
+
+    bool render_water = gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_VOIDWATER);
+    F32 water_z = render_water ? land.getWaterHeight() + 0.01f : 0;
 
     LLGLSUIDefault gls_ui; // called from pipeline
     gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
-- 
cgit v1.2.3


From b6bf32e00e25de773b6403b716c655f64b6092c9 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 19 Mar 2025 17:57:50 +0200
Subject: #3488 Prelock mutexes

Main thread has priority, it shouldn't be relocking on each loadMeshLOD
---
 indra/newview/llmeshrepository.cpp | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'indra')

diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index e1fa84b4d8..a8c6f69425 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -4573,6 +4573,8 @@ void LLMeshRepository::notifyLoadedMeshes()
                 std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count,
                                   mPendingRequests.end(), PendingRequestBase::CompareScoreGreater());
             }
+            LLMutexTrylock lock3(mThread->mHeaderMutex);
+            LLMutexTrylock lock4(mThread->mPendingMutex);
             while (!mPendingRequests.empty() && push_count > 0)
             {
                 std::unique_ptr<PendingRequestBase>& req_p = mPendingRequests.front();
-- 
cgit v1.2.3


From dee3257ffd9735f0cdaec643743b9dd8cd1920b3 Mon Sep 17 00:00:00 2001
From: Maxim Nikolenko <maximnproductengine@lindenlab.com>
Date: Thu, 20 Mar 2025 15:15:42 +0200
Subject: #1964 fix for reflection probe doesn't update position when the
 linkset is moved by the script

---
 indra/newview/llviewerobject.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

(limited to 'indra')

diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 030dfeaa6f..3d88cc5f39 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -4820,6 +4820,18 @@ void LLViewerObject::setPositionParent(const LLVector3 &pos_parent, bool damped)
     else
     {
         setPositionRegion(pos_parent, damped);
+
+        // #1964 mark reflection probe in the linkset to update position after moving via script
+        for (LLViewerObject* child : mChildList)
+        {
+            if (child && child->isReflectionProbe())
+            {
+                if (LLDrawable* drawablep = child->mDrawable)
+                {
+                    gPipeline.markMoved(drawablep);
+                }
+            }
+        }
     }
 }
 
-- 
cgit v1.2.3


From c695ae285fc14f1cce50a1cf1757b3a6e38308b8 Mon Sep 17 00:00:00 2001
From: Maxim Nikolenko <maximnproductengine@lindenlab.com>
Date: Thu, 20 Mar 2025 21:10:06 +0200
Subject: #3779 reset texture repeats when unchecking the Hide Water option

---
 indra/newview/llpanelface.cpp | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'indra')

diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
index f491cccaf8..c4f118fed8 100644
--- a/indra/newview/llpanelface.cpp
+++ b/indra/newview/llpanelface.cpp
@@ -3077,6 +3077,8 @@ void LLPanelFace::onCommitHideWater()
     {
         // reset texture to default plywood
         LLSelectMgr::getInstance()->selectionSetImage(DEFAULT_OBJECT_TEXTURE);
+        // reset texture repeats, that might be altered by invisiprim script from wiki
+        LLSelectMgr::getInstance()->selectionTexScaleAutofit(2.f);
     }
 }
 
-- 
cgit v1.2.3


From bb914ab5e368aa6265aeb057990074f6f275adbd Mon Sep 17 00:00:00 2001
From: Andrey Lihatskiy <alihatskiy@productengine.com>
Date: Fri, 21 Mar 2025 00:16:14 +0200
Subject: #1644 Correct render type for on-water property lines

---
 indra/newview/llviewerparceloverlay.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'indra')

diff --git a/indra/newview/llviewerparceloverlay.cpp b/indra/newview/llviewerparceloverlay.cpp
index 19066217b1..da03d3b015 100755
--- a/indra/newview/llviewerparceloverlay.cpp
+++ b/indra/newview/llviewerparceloverlay.cpp
@@ -667,7 +667,7 @@ void LLViewerParcelOverlay::renderPropertyLines()
 
     LLSurface& land = mRegion->getLand();
 
-    bool render_water = gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_VOIDWATER);
+    bool render_water = gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_WATER);
     F32 water_z = render_water ? land.getWaterHeight() + 0.01f : 0;
 
     LLGLSUIDefault gls_ui; // called from pipeline
-- 
cgit v1.2.3


From d2dd881a5c840b5132396d6e8353b38b79d69e23 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 20 Mar 2025 19:37:25 +0200
Subject: #3778 #3654 Split text and background selection colors

---
 indra/llui/llbutton.cpp                |  2 +-
 indra/llui/llmenugl.cpp                |  2 +-
 indra/llui/llscrolllistctrl.cpp        |  2 +-
 indra/llui/llsearchablecontrol.h       | 10 ++++++++--
 indra/llui/lltextbase.cpp              |  2 +-
 indra/newview/skins/default/colors.xml |  5 ++++-
 6 files changed, 16 insertions(+), 7 deletions(-)

(limited to 'indra')

diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
index 1bce31edb1..d2534b3939 100644
--- a/indra/llui/llbutton.cpp
+++ b/indra/llui/llbutton.cpp
@@ -837,7 +837,7 @@ void LLButton::draw()
 
     // Highlight if needed
     if( ll::ui::SearchableControl::getHighlighted() )
-        label_color = ll::ui::SearchableControl::getHighlightColor();
+        label_color = ll::ui::SearchableControl::getHighlightFontColor();
 
     // overlay with keyboard focus border
     if (hasFocus())
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index 69ffa9a94f..c11b42a348 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -508,7 +508,7 @@ void LLMenuItemGL::draw( void )
 
     // Highlight if needed
     if( ll::ui::SearchableControl::getHighlighted() )
-        color = ll::ui::SearchableControl::getHighlightColor();
+        color = ll::ui::SearchableControl::getHighlightFontColor();
 
     // Draw the text on top.
     if (mBriefItem)
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 943aff1ca1..245339b107 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -3394,7 +3394,7 @@ bool LLScrollListCtrl::highlightMatchingItems(const std::string& filter_str)
 
     bool res = false;
 
-    setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red4));
+    setHighlightedColor(LLUIColorTable::instance().getColor("SearchableControlHighlightBgColor", LLColor4::red4));
 
     std::string filter_str_lc(filter_str);
     LLStringUtil::toLower(filter_str_lc);
diff --git a/indra/llui/llsearchablecontrol.h b/indra/llui/llsearchablecontrol.h
index 7f1421dd19..bae85fe9a5 100644
--- a/indra/llui/llsearchablecontrol.h
+++ b/indra/llui/llsearchablecontrol.h
@@ -43,9 +43,15 @@ namespace ll
             virtual ~SearchableControl()
             { }
 
-            const LLColor4& getHighlightColor( ) const
+            const LLColor4& getHighlightBgColor( ) const
             {
-                static LLUIColor highlight_color = LLUIColorTable::instance().getColor("SearchableControlHighlightColor", LLColor4::red4);
+                static LLUIColor highlight_color = LLUIColorTable::instance().getColor("SearchableControlHighlightBgColor", LLColor4::red4);
+                return highlight_color.get();
+            }
+
+            const LLColor4& getHighlightFontColor() const
+            {
+                static LLUIColor highlight_color = LLUIColorTable::instance().getColor("SearchableControlHighlightFontColor", LLColor4::red4);
                 return highlight_color.get();
             }
 
diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp
index fae22fd248..41e7094163 100644
--- a/indra/llui/lltextbase.cpp
+++ b/indra/llui/lltextbase.cpp
@@ -1378,7 +1378,7 @@ void LLTextBase::draw()
     // Draw highlighted if needed
     if( ll::ui::SearchableControl::getHighlighted() )
     {
-        const LLColor4& bg_color = ll::ui::SearchableControl::getHighlightColor();
+        const LLColor4& bg_color = ll::ui::SearchableControl::getHighlightBgColor();
         LLRect bg_rect = mVisibleTextRect;
         if( mScroller )
             bg_rect.intersectWith( text_rect );
diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml
index e17321e698..f0af4acf20 100644
--- a/indra/newview/skins/default/colors.xml
+++ b/indra/newview/skins/default/colors.xml
@@ -759,7 +759,10 @@
      name="SelectedOutfitTextColor"
      reference="EmphasisColor" />
     <color
-     name="SearchableControlHighlightColor"
+     name="SearchableControlHighlightFontColor"
+     value="1 0 0 1" />
+    <color
+     name="SearchableControlHighlightBgColor"
      value="0.5 0.1 0.1 1" />
     <color
      name="SilhouetteChildColor"
-- 
cgit v1.2.3


From 18e06771e56b77d4a1bb875734354612122d05c5 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Wed, 12 Mar 2025 15:59:48 +0200
Subject: #3364 Fix a case of excessive texture updates

when bias fluctuates a bit.
Make bias' effect a bit more gradual.
---
 indra/newview/lldrawpooltree.cpp      |  5 +++--
 indra/newview/llviewertexture.cpp     | 15 ++++++++++++---
 indra/newview/llviewertexturelist.cpp |  3 ++-
 3 files changed, 17 insertions(+), 6 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp
index 6efd503574..26ef190fbb 100644
--- a/indra/newview/lldrawpooltree.cpp
+++ b/indra/newview/lldrawpooltree.cpp
@@ -108,8 +108,9 @@ void LLDrawPoolTree::beginShadowPass(S32 pass)
 {
     LL_PROFILE_ZONE_SCOPED;
 
-    glPolygonOffset(gSavedSettings.getF32("RenderDeferredTreeShadowOffset"),
-                    gSavedSettings.getF32("RenderDeferredTreeShadowBias"));
+    static LLCachedControl<F32> shadow_offset(gSavedSettings, "RenderDeferredTreeShadowOffset");
+    static LLCachedControl<F32> shadow_bias(gSavedSettings, "RenderDeferredTreeShadowBias");
+    glPolygonOffset(shadow_offset(), shadow_bias());
 
     LLEnvironment& environment = LLEnvironment::instance();
 
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index a0723db479..0782192858 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -519,7 +519,6 @@ void LLViewerTexture::updateClass()
 
     bool is_sys_low = isSystemMemoryLow();
     bool is_low = is_sys_low || over_pct > 0.f;
-    F32 discard_bias = sDesiredDiscardBias;
 
     static bool was_low = false;
     static bool was_sys_low = false;
@@ -571,6 +570,7 @@ void LLViewerTexture::updateClass()
 
     // set to max discard bias if the window has been backgrounded for a while
     static F32 last_desired_discard_bias = 1.f;
+    static F32 last_texture_update_count_bias = 1.f;
     static bool was_backgrounded = false;
     static LLFrameTimer backgrounded_timer;
     static LLCachedControl<F32> minimized_discard_time(gSavedSettings, "TextureDiscardMinimizedTime", 1.f);
@@ -606,12 +606,21 @@ void LLViewerTexture::updateClass()
     }
 
     sDesiredDiscardBias = llclamp(sDesiredDiscardBias, 1.f, 4.f);
-    if (discard_bias != sDesiredDiscardBias)
+    if (last_texture_update_count_bias < sDesiredDiscardBias)
     {
-        // bias changed, reset texture update counter to
+        // bias increased, reset texture update counter to
         // let updates happen at an increased rate.
+        last_texture_update_count_bias = sDesiredDiscardBias;
         sBiasTexturesUpdated = 0;
     }
+    else if (last_texture_update_count_bias > sDesiredDiscardBias + 0.1f)
+    {
+        // bias decreased, 0.1f is there to filter out small fluctuations
+        // and not reset sBiasTexturesUpdated too often.
+        // Bias jumps to 1.5 at low memory, so getting stuck at 1.1 is not
+        // a problem.
+        last_texture_update_count_bias = sDesiredDiscardBias;
+    }
 
     LLViewerTexture::sFreezeImageUpdates = false;
 }
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index ae723b4068..db530a3943 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -958,7 +958,8 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
                     vsize /= min_scale;
 
                     // apply bias to offscreen faces all the time, but only to onscreen faces when bias is large
-                    if (!face->mInFrustum || LLViewerTexture::sDesiredDiscardBias > 2.f)
+                    // use mImportanceToCamera to make bias switch a bit more gradual
+                    if (!face->mInFrustum || LLViewerTexture::sDesiredDiscardBias > 1.9f + face->mImportanceToCamera / 2.f)
                     {
                         vsize /= bias;
                     }
-- 
cgit v1.2.3


From 32c7d3064f04899547ee4dea48969c6ceb8554e9 Mon Sep 17 00:00:00 2001
From: "Jonathan \"Geenz\" Goodman" <geenz@lindenlab.com>
Date: Fri, 21 Mar 2025 09:33:23 -0400
Subject: Dynamic Probe Allocation (#3787)

* #3788 Support dynamic probe allocation.
* #3738 Mitigate probe flashing
* #3735 Mitigate realtime probes flashing
---
 .../class1/windlight/atmosphericsFuncs.glsl        |  1 +
 indra/newview/featuretable.txt                     | 14 +---
 indra/newview/featuretable_mac.txt                 | 10 ---
 indra/newview/llreflectionmap.cpp                  |  3 +
 indra/newview/llreflectionmapmanager.cpp           | 86 ++++++++++++++++------
 indra/newview/llreflectionmapmanager.h             | 11 ++-
 indra/newview/lltextureview.cpp                    |  4 +-
 indra/newview/llviewertexture.cpp                  |  7 +-
 .../en/floater_preferences_graphics_advanced.xml   | 17 -----
 9 files changed, 87 insertions(+), 66 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl
index 205d4bff6d..5089b9e31e 100644
--- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl
+++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl
@@ -154,6 +154,7 @@ void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, ou
     if (classic_mode < 1)
     {
         amblit = srgb_to_linear(amblit);
+        amblit = vec3(dot(amblit, vec3(0.2126, 0.7152, 0.0722)));
         sunlit = srgb_to_linear(sunlit);
     }
 
diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt
index 883963d558..443a7ae914 100644
--- a/indra/newview/featuretable.txt
+++ b/indra/newview/featuretable.txt
@@ -86,7 +86,6 @@ RenderTonemapType			1   1
 RenderTonemapMix			1   1
 RenderDisableVintageMode           1   1
 RenderMaxTextureResolution         1   2048
-RenderReflectionProbeCount  1   256
 
 //
 // Low Graphics Settings
@@ -129,7 +128,6 @@ RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderDisableVintageMode           1   0
 RenderMaxTextureResolution         1   512
-RenderReflectionProbeCount  1   1
 
 //
 // Medium Low Graphics Settings
@@ -172,7 +170,6 @@ RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderDisableVintageMode           1   0
 RenderMaxTextureResolution         1   1024
-RenderReflectionProbeCount  1   16
 
 //
 // Medium Graphics Settings (standard)
@@ -214,7 +211,6 @@ RenderExposure				1   1
 RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderMaxTextureResolution  1   2048
-RenderReflectionProbeCount  1   32
 
 //
 // Medium High Graphics Settings
@@ -245,7 +241,7 @@ RenderFSAASamples			1	1
 RenderReflectionsEnabled    1   1
 RenderReflectionProbeDetail	1	1
 RenderScreenSpaceReflections 1  0
-RenderReflectionProbeLevel  1   2
+RenderReflectionProbeLevel  1   1
 RenderMirrors				1	0
 RenderHeroProbeResolution	1	512
 RenderHeroProbeDistance		1	6
@@ -256,7 +252,6 @@ RenderExposure				1   1
 RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderMaxTextureResolution  1   2048
-RenderReflectionProbeCount  1   64
 
 //
 // High Graphics Settings (SSAO + sun shadows)
@@ -287,7 +282,7 @@ RenderFSAASamples			1	2
 RenderReflectionsEnabled    1   1
 RenderReflectionProbeDetail	1	1
 RenderScreenSpaceReflections 1  0
-RenderReflectionProbeLevel  1   3
+RenderReflectionProbeLevel  1   2
 RenderMirrors				1	0
 RenderHeroProbeResolution	1	512
 RenderHeroProbeDistance		1	8
@@ -298,7 +293,6 @@ RenderExposure				1   1
 RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderMaxTextureResolution  1   2048
-RenderReflectionProbeCount  1   128
 
 //
 // High Ultra Graphics Settings (deferred + SSAO + all shadows)
@@ -340,7 +334,6 @@ RenderExposure				1   1
 RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderMaxTextureResolution  1   2048
-RenderReflectionProbeCount  1   256
 
 //
 // Ultra graphics (REALLY PURTY!)
@@ -382,7 +375,6 @@ RenderExposure				1   1
 RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderMaxTextureResolution  1   2048
-RenderReflectionProbeCount  1   256
 
 //
 // Class Unknown Hardware (unknown)
@@ -416,7 +408,6 @@ RenderReflectionProbeDetail	0	-1
 RenderMirrors				0	0
 RenderDisableVintageMode           1   0
 RenderMaxTextureResolution         1   2048
-RenderReflectionProbeCount  0   0
 
 list Intel
 RenderAnisotropic			1	0
@@ -438,7 +429,6 @@ RenderMirrors				0	0
 RenderGLMultiThreadedTextures 0 0
 RenderGLMultiThreadedMedia 0 0
 RenderDisableVintageMode           1   0
-RenderReflectionProbeCount  0   0
 
 list TexUnit16orLess
 RenderTerrainPBRDetail      1   -1
diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt
index 103d24a26d..dc9473b042 100644
--- a/indra/newview/featuretable_mac.txt
+++ b/indra/newview/featuretable_mac.txt
@@ -86,7 +86,6 @@ RenderTonemapMix			1   1
 RenderDisableVintageMode           1   1
 RenderDownScaleMethod       1   0
 RenderMaxTextureResolution         1   2048
-RenderReflectionProbeCount  1   256
 
 //
 // Low Graphics Settings
@@ -129,7 +128,6 @@ RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderDisableVintageMode           1   0
 RenderMaxTextureResolution         1   512
-RenderReflectionProbeCount  1   1
 
 //
 // Medium Low Graphics Settings
@@ -172,7 +170,6 @@ RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderDisableVintageMode           1   0
 RenderMaxTextureResolution         1   1024
-RenderReflectionProbeCount  1   16
 
 //
 // Medium Graphics Settings (standard)
@@ -214,7 +211,6 @@ RenderExposure				1   1
 RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderMaxTextureResolution  1   2048
-RenderReflectionProbeCount  1   32
 
 //
 // Medium High Graphics Settings
@@ -256,7 +252,6 @@ RenderExposure				1   1
 RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderMaxTextureResolution  1   2048
-RenderReflectionProbeCount  1   64
 
 //
 // High Graphics Settings (SSAO + sun shadows)
@@ -298,7 +293,6 @@ RenderExposure				1   1
 RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderMaxTextureResolution  1   2048
-RenderReflectionProbeCount  1   128
 
 //
 // High Ultra Graphics Settings (SSAO + all shadows)
@@ -340,7 +334,6 @@ RenderExposure				1   1
 RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderMaxTextureResolution  1   2048
-RenderReflectionProbeCount  1   256
 
 //
 // Ultra graphics (REALLY PURTY!)
@@ -382,7 +375,6 @@ RenderExposure				1   1
 RenderTonemapType			1   1
 RenderTonemapMix			1   0.7
 RenderMaxTextureResolution  1   2048
-RenderReflectionProbeCount  1   256
 
 //
 // Class Unknown Hardware (unknown)
@@ -415,7 +407,6 @@ RenderShadowDetail			0	0
 RenderMirrors				0	0
 RenderDisableVintageMode           1   0
 RenderMaxTextureResolution         1   2048
-RenderReflectionProbeCount  0   0
 
 list TexUnit8orLess
 RenderDeferredSSAO			0	0
@@ -456,7 +447,6 @@ RenderReflectionProbeDetail	0	0
 RenderReflectionsEnabled    0   0
 RenderMirrors				0	0
 RenderDisableVintageMode           1   0
-RenderReflectionProbeCount  0   0
 
 list VaryingVectors16orLess
 RenderTerrainPBRPlanarSampleCount 1   1
diff --git a/indra/newview/llreflectionmap.cpp b/indra/newview/llreflectionmap.cpp
index f3adb52d5e..910509928d 100644
--- a/indra/newview/llreflectionmap.cpp
+++ b/indra/newview/llreflectionmap.cpp
@@ -52,6 +52,9 @@ LLReflectionMap::~LLReflectionMap()
 void LLReflectionMap::update(U32 resolution, U32 face, bool force_dynamic, F32 near_clip, bool useClipPlane, LLPlane clipPlane)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_DISPLAY;
+    if (!mCubeArray.notNull())
+        return;
+
     mLastUpdateTime = gFrameTimeSeconds;
     llassert(mCubeArray.notNull());
     llassert(mCubeIndex != -1);
diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp
index 48b73531ea..34c6034e9e 100644
--- a/indra/newview/llreflectionmapmanager.cpp
+++ b/indra/newview/llreflectionmapmanager.cpp
@@ -144,13 +144,14 @@ static void touch_default_probe(LLReflectionMap* probe)
 
 LLReflectionMapManager::LLReflectionMapManager()
 {
+    mDynamicProbeCount = LL_MAX_REFLECTION_PROBE_COUNT;
     initCubeFree();
 }
 
 void LLReflectionMapManager::initCubeFree()
 {
     // start at 1 because index 0 is reserved for mDefaultProbe
-    for (int i = 1; i < LL_MAX_REFLECTION_PROBE_COUNT; ++i)
+    for (U32 i = 1; i < mDynamicProbeCount; ++i)
     {
         mCubeFree.push_back(i);
     }
@@ -221,15 +222,50 @@ void LLReflectionMapManager::update()
         resume();
     }
 
-    static LLCachedControl<U32> probe_count(gSavedSettings, "RenderReflectionProbeCount", 256U);
-    bool countReset = mReflectionProbeCount != probe_count;
+    static LLCachedControl<S32> sDetail(gSavedSettings, "RenderReflectionProbeDetail", -1);
+    static LLCachedControl<S32> sLevel(gSavedSettings, "RenderReflectionProbeLevel", 3);
 
-    if (countReset)
+    // Once every 20 frames, update the dynamic probe count.
+    if (gFrameCount % 20)
     {
-        mResetFade = -0.5f;
+        U32 probe_count_temp = mDynamicProbeCount;
+        if (sLevel == 0)
+        {
+            mDynamicProbeCount = 1;
+        }
+        else if (sLevel == 1)
+        {
+            mDynamicProbeCount = (U32)mProbes.size();
+
+        }
+        else if (sLevel == 2)
+        {
+            mDynamicProbeCount = llmax((U32)mProbes.size(), 128);
+        }
+        else
+        {
+            mDynamicProbeCount = 256;
+        }
+
+        // Round mDynamicProbeCount to the nearest increment of 32
+        mDynamicProbeCount = ((mDynamicProbeCount + 16) / 32) * 32;
+        mDynamicProbeCount = llclamp(mDynamicProbeCount, 1, LL_MAX_REFLECTION_PROBE_COUNT);
+
+        if (mDynamicProbeCount < probe_count_temp * 1.1 && mDynamicProbeCount > probe_count_temp * 0.9)
+            mDynamicProbeCount = probe_count_temp;
+        else
+            mGlobalFadeTarget = 0.f;
     }
 
-    initReflectionMaps();
+    if (mGlobalFadeTarget < mResetFade)
+        mResetFade = llmax(mGlobalFadeTarget, mResetFade - (F32)gFrameIntervalSeconds * 2);
+    else
+        mResetFade = llmin(mGlobalFadeTarget, mResetFade + (F32)gFrameIntervalSeconds * 2);
+
+    if (mResetFade == mGlobalFadeTarget)
+    {
+        initReflectionMaps();
+    }
 
     static LLCachedControl<bool> render_hdr(gSavedSettings, "RenderHDREnabled", true);
 
@@ -286,9 +322,6 @@ void LLReflectionMapManager::update()
 
     bool did_update = false;
 
-    static LLCachedControl<S32> sDetail(gSavedSettings, "RenderReflectionProbeDetail", -1);
-    static LLCachedControl<S32> sLevel(gSavedSettings, "RenderReflectionProbeLevel", 3);
-
     bool realtime = sDetail >= (S32)LLReflectionMapManager::DetailLevel::REALTIME;
 
     LLReflectionMap* closestDynamic = nullptr;
@@ -343,12 +376,7 @@ void LLReflectionMapManager::update()
         }
     }
 
-    if (countReset)
-    {
-        mResetFade = -0.5f;
-    }
-
-    mResetFade = llmin((F32)(mResetFade + gFrameIntervalSeconds), 1.f);
+    mResetFade = llmin((F32)(mResetFade + gFrameIntervalSeconds * 2.f), 1.f);
 
     for (unsigned int i = 0; i < mProbes.size(); ++i)
     {
@@ -520,6 +548,16 @@ LLReflectionMap* LLReflectionMapManager::addProbe(LLSpatialGroup* group)
     return probe;
 }
 
+U32 LLReflectionMapManager::probeCount()
+{
+    return mDynamicProbeCount;
+}
+
+U32 LLReflectionMapManager::probeMemory()
+{
+    return (mDynamicProbeCount * 6 * (mProbeResolution * mProbeResolution) * 4) / 1024 / 1024 + (mDynamicProbeCount * 6 * (LL_IRRADIANCE_MAP_RESOLUTION * LL_IRRADIANCE_MAP_RESOLUTION) * 4) / 1024 / 1024;
+}
+
 struct CompareProbeDepth
 {
     bool operator()(const LLReflectionMap* lhs, const LLReflectionMap* rhs)
@@ -1058,7 +1096,11 @@ void LLReflectionMapManager::updateUniforms()
 
     bool is_ambiance_pass = gCubeSnapshot && !isRadiancePass();
     F32 ambscale = is_ambiance_pass ? 0.f : 1.f;
+    ambscale *= mResetFade;
+    ambscale = llmax(0, ambscale);
     F32 radscale = is_ambiance_pass ? 0.5f : 1.f;
+    radscale *= mResetFade;
+    radscale = llmax(0, radscale);
 
     for (auto* refmap : mReflectionMaps)
     {
@@ -1129,8 +1171,8 @@ void LLReflectionMapManager::updateUniforms()
         }
 
         mProbeData.refParams[count].set(
-            llmax(minimum_ambiance, refmap->getAmbiance())*ambscale * llmax(mResetFade, 0.f), // ambiance scale
-            radscale * llmax(mResetFade, 0.f), // radiance scale
+            llmax(minimum_ambiance, refmap->getAmbiance())*ambscale, // ambiance scale
+            radscale, // radiance scale
             refmap->mFadeIn, // fade in weight
             oa.getF32ptr()[2] - refmap->mRadius); // z near
 
@@ -1365,12 +1407,9 @@ void LLReflectionMapManager::renderDebug()
 
 void LLReflectionMapManager::initReflectionMaps()
 {
-    static LLCachedControl<U32> probe_count(gSavedSettings, "RenderReflectionProbeCount", 256U);
-    U32 count = probe_count();
-
     static LLCachedControl<U32> ref_probe_res(gSavedSettings, "RenderReflectionProbeResolution", 128U);
     U32 probe_resolution = nhpo2(llclamp(ref_probe_res(), (U32)64, (U32)512));
-    if (mTexture.isNull() || mReflectionProbeCount != count || mProbeResolution != probe_resolution || mReset)
+    if (mTexture.isNull() || mReflectionProbeCount != mDynamicProbeCount || mProbeResolution != probe_resolution || mReset)
     {
         if(mProbeResolution != probe_resolution)
         {
@@ -1379,9 +1418,10 @@ void LLReflectionMapManager::initReflectionMaps()
         }
 
         gEXRImage = nullptr;
-
+        mGlobalFadeTarget = 1.f;
+        mResetFade = -0.125f;
         mReset = false;
-        mReflectionProbeCount = count;
+        mReflectionProbeCount = mDynamicProbeCount;
         mProbeResolution = probe_resolution;
         mMaxProbeLOD = log2f((F32)mProbeResolution) - 1.f; // number of mips - 1
 
diff --git a/indra/newview/llreflectionmapmanager.h b/indra/newview/llreflectionmapmanager.h
index 9f88776ac2..0719c28134 100644
--- a/indra/newview/llreflectionmapmanager.h
+++ b/indra/newview/llreflectionmapmanager.h
@@ -38,7 +38,7 @@ class LLViewerObject;
 #define LL_MAX_REFLECTION_PROBE_COUNT 256
 
 // reflection probe resolution
-#define LL_IRRADIANCE_MAP_RESOLUTION 64
+#define LL_IRRADIANCE_MAP_RESOLUTION 16
 
 // reflection probe mininum scale
 #define LL_REFLECTION_PROBE_MINIMUM_SCALE 1.f;
@@ -159,6 +159,9 @@ public:
     // with false when done.
     void forceDefaultProbeAndUpdateUniforms(bool force = true);
 
+    U32 probeCount();
+    U32 probeMemory();
+
 private:
     friend class LLPipeline;
     friend class LLHeroProbeManager;
@@ -166,6 +169,9 @@ private:
     // initialize mCubeFree array to default values
     void initCubeFree();
 
+    // Just does a bulk clear of all of the cubemaps.
+    void clearCubeMaps();
+
     // delete the probe with the given index in mProbes
     void deleteProbe(U32 i);
 
@@ -240,6 +246,8 @@ private:
     // number of reflection probes to use for rendering
     U32 mReflectionProbeCount;
 
+    U32 mDynamicProbeCount;
+
     // resolution of reflection probes
     U32 mProbeResolution = 128;
 
@@ -253,6 +261,7 @@ private:
     bool mReset = false;
 
     float mResetFade = 1.f;
+    float mGlobalFadeTarget = 1.f;
 
     // if true, only update the default probe
     bool mPaused = false;
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index 78d930c05c..8560a01c4b 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -559,10 +559,12 @@ void LLGLTexMemBar::draw()
     gGL.color4f(0.f, 0.f, 0.f, 0.25f);
     gl_rect_2d(-10, getRect().getHeight() + line_height*2 + 1, getRect().getWidth()+2, getRect().getHeight()+2);
 
-    text = llformat("Est. Free: %d MB Sys Free: %d MB FBO: %d MB Bias: %.2f Cache: %.1f/%.1f MB",
+    text = llformat("Est. Free: %d MB Sys Free: %d MB FBO: %d MB Probe#: %d Probe Mem: %d MB Bias: %.2f Cache: %.1f/%.1f MB",
                     (S32)LLViewerTexture::sFreeVRAMMegabytes,
                     LLMemory::getAvailableMemKB()/1024,
                     LLRenderTarget::sBytesAllocated/(1024*1024),
+                    gPipeline.mReflectionMapManager.probeCount(),
+                    gPipeline.mReflectionMapManager.probeMemory(),
                     discard_bias,
                     cache_usage,
                     cache_max_usage);
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 0782192858..4a9dd1c1b6 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -504,8 +504,11 @@ void LLViewerTexture::updateClass()
     // NOTE: our metrics miss about half the vram we use, so this biases high but turns out to typically be within 5% of the real number
     F32 used = (F32)ll_round(texture_bytes_alloc + vertex_bytes_alloc);
 
-    F32 budget = max_vram_budget == 0 ? (F32)gGLManager.mVRAM : (F32)max_vram_budget;
-    budget /= tex_vram_divisor;
+    // For debugging purposes, it's useful to be able to set the VRAM budget manually.
+    // But when manual control is not enabled, use the VRAM divisor.
+    // While we're at it, assume we have 1024 to play with at minimum when the divisor is in use.  Works more elegantly with the logic below this.
+    // -Geenz 2025-03-21
+    F32 budget = max_vram_budget == 0 ? llmax(1024, (F32)gGLManager.mVRAM / tex_vram_divisor) : (F32)max_vram_budget;
 
     // Try to leave at least half a GB for everyone else and for bias,
     // but keep at least 768MB for ourselves
diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
index 34ed3fa80d..78d13293a8 100644
--- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
+++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
@@ -858,23 +858,6 @@
       value="3"/>
   </combo_box>
 
-  <slider
-    control_name="RenderReflectionProbeCount"
-    decimal_digits="0"
-    follows="left|top"
-    height="16"
-    increment="1"
-    initial_value="256"
-    label="Max. Reflection Probes:"
-    label_width="145"
-    layout="topleft"
-    left="420"
-    min_val="1"
-    max_val="256"
-    name="MaxProbes"
-    top_delta="24"
-    width="260" />
-
   <slider
     control_name="RenderExposure"
     decimal_digits="1"
-- 
cgit v1.2.3


From 0ec9bfaabde12d82ba6b840589f849b7cf279417 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 21 Mar 2025 16:22:47 +0200
Subject: #3713 Crash at updateGLTFMaterials

---
 indra/newview/llfetchedgltfmaterial.cpp |  1 +
 indra/newview/lllocalbitmaps.cpp        | 72 +++++++++++++++++----------------
 indra/newview/lllocalbitmaps.h          |  2 +-
 3 files changed, 39 insertions(+), 36 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llfetchedgltfmaterial.cpp b/indra/newview/llfetchedgltfmaterial.cpp
index c2821d56d6..558fc92018 100644
--- a/indra/newview/llfetchedgltfmaterial.cpp
+++ b/indra/newview/llfetchedgltfmaterial.cpp
@@ -199,6 +199,7 @@ bool LLFetchedGLTFMaterial::replaceLocalTexture(const LLUUID& tracking_id, const
     {
         mTrackingIdToLocalTexture.erase(tracking_id);
     }
+    updateLocalTexDataDigest();
 
     return res;
 }
diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp
index 31c9eb8966..f08582e860 100644
--- a/indra/newview/lllocalbitmaps.cpp
+++ b/indra/newview/lllocalbitmaps.cpp
@@ -293,8 +293,9 @@ void LLLocalBitmap::addGLTFMaterial(LLGLTFMaterial* mat)
         return;
     }
 
-    mat_list_t::iterator end = mGLTFMaterialWithLocalTextures.end();
-    for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != end;)
+    mat->addLocalTextureTracking(getTrackingID(), getWorldID());
+
+    for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != mGLTFMaterialWithLocalTextures.end();)
     {
         if (it->get() == mat)
         {
@@ -304,15 +305,12 @@ void LLLocalBitmap::addGLTFMaterial(LLGLTFMaterial* mat)
         if ((*it)->getNumRefs() == 1)
         {
             it = mGLTFMaterialWithLocalTextures.erase(it);
-            end = mGLTFMaterialWithLocalTextures.end();
         }
         else
         {
             it++;
         }
     }
-
-    mat->addLocalTextureTracking(getTrackingID(), getWorldID());
     mGLTFMaterialWithLocalTextures.push_back(mat);
 }
 
@@ -628,16 +626,16 @@ void LLLocalBitmap::updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableTyp
 void LLLocalBitmap::updateGLTFMaterials(LLUUID old_id, LLUUID new_id)
 {
     // Might be a better idea to hold this in LLGLTFMaterialList
-    mat_list_t::iterator end = mGLTFMaterialWithLocalTextures.end();
-    for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != end;)
+    for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != mGLTFMaterialWithLocalTextures.end();)
     {
         if ((*it)->getNumRefs() == 1)
         {
             // render and override materials are often recreated,
             // clean up any remains
             it = mGLTFMaterialWithLocalTextures.erase(it);
-            end = mGLTFMaterialWithLocalTextures.end();
         }
+        // Render material consists of base and override materials, make sure replaceLocalTexture
+        // gets called for base and override before applyOverride
         else if ((*it)->replaceLocalTexture(mTrackingID, old_id, new_id))
         {
             it++;
@@ -647,43 +645,47 @@ void LLLocalBitmap::updateGLTFMaterials(LLUUID old_id, LLUUID new_id)
             // Matching id not found, no longer in use
             // material would clean itself, remove from the list
             it = mGLTFMaterialWithLocalTextures.erase(it);
-            end = mGLTFMaterialWithLocalTextures.end();
         }
     }
 
-    // Render material consists of base and override materials, make sure replaceLocalTexture
-    // gets called for base and override before applyOverride
-    end = mGLTFMaterialWithLocalTextures.end();
-    for (mat_list_t::iterator it = mGLTFMaterialWithLocalTextures.begin(); it != end;)
+    // Updating render materials calls updateTextureTracking which can modify
+    // mGLTFMaterialWithLocalTextures, so precollect all entries that need to be updated
+    std::set<LLTextureEntry*> update_entries;
+    for (LLGLTFMaterial* mat : mGLTFMaterialWithLocalTextures)
     {
-        LLFetchedGLTFMaterial* fetched_mat = dynamic_cast<LLFetchedGLTFMaterial*>((*it).get());
+        // mGLTFMaterialWithLocalTextures includes overrides that are not 'fetched'
+        // and don't have texture entries (they don't need to since render material does).
+        LLFetchedGLTFMaterial* fetched_mat = dynamic_cast<LLFetchedGLTFMaterial*>(mat);
         if (fetched_mat)
         {
             for (LLTextureEntry* entry : fetched_mat->mTextureEntires)
             {
-                // Normally a change in applied material id is supposed to
-                // drop overrides thus reset material, but local materials
-                // currently reuse their existing asset id, and purpose is
-                // to preview how material will work in-world, overrides
-                // included, so do an override to render update instead.
-                LLGLTFMaterial* override_mat = entry->getGLTFMaterialOverride();
-                if (override_mat)
-                {
-                    // do not create a new material, reuse existing pointer
-                    LLFetchedGLTFMaterial* render_mat = dynamic_cast<LLFetchedGLTFMaterial*>(entry->getGLTFRenderMaterial());
-                    if (render_mat)
-                    {
-                            *render_mat = *fetched_mat;
-                        render_mat->applyOverride(*override_mat);
-                    }
-                    else
-                    {
-                        LL_WARNS_ONCE() << "Failed to apply local material override, render material not found" << LL_ENDL;
-                    }
-                }
+                update_entries.insert(entry);
+            }
+        }
+    }
+
+
+    for (LLTextureEntry* entry : update_entries)
+    {
+        // Normally a change in applied material id is supposed to
+        // drop overrides thus reset material, but local materials
+        // currently reuse their existing asset id, and purpose is
+        // to preview how material will work in-world, overrides
+        // included, so do an override to render update instead.
+        LLGLTFMaterial* override_mat = entry->getGLTFMaterialOverride();
+        LLGLTFMaterial* mat = entry->getGLTFMaterial();
+        if (override_mat && mat)
+        {
+            // do not create a new material, reuse existing pointer
+            // so that mTextureEntires remains untouched
+            LLGLTFMaterial* render_mat = entry->getGLTFRenderMaterial();
+            if (render_mat)
+            {
+                *render_mat = *mat;
+                render_mat->applyOverride(*override_mat); // can update mGLTFMaterialWithLocalTextures
             }
         }
-        ++it;
     }
 }
 
diff --git a/indra/newview/lllocalbitmaps.h b/indra/newview/lllocalbitmaps.h
index e169f96e70..de2dcb3467 100644
--- a/indra/newview/lllocalbitmaps.h
+++ b/indra/newview/lllocalbitmaps.h
@@ -106,7 +106,7 @@ class LLLocalBitmap
 
         // Store a list of accosiated materials
         // Might be a better idea to hold this in LLGLTFMaterialList
-        typedef std::vector<LLPointer<LLGLTFMaterial> > mat_list_t;
+        typedef std::list<LLPointer<LLGLTFMaterial> > mat_list_t;
         mat_list_t mGLTFMaterialWithLocalTextures;
 
 };
-- 
cgit v1.2.3