From f3b8565d212a29a04082d65fc45ab0aa48af3e64 Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Fri, 6 Oct 2023 23:27:11 +0300
Subject: [PATCH] SL-20411 Don't load all thumnails at once for large folders
 #2

---
 indra/newview/llinventorygallery.cpp | 26 ++++++++--
 indra/newview/llinventorygallery.h   |  4 ++
 indra/newview/llthumbnailctrl.cpp    | 77 ++++++++++++++++++++--------
 indra/newview/llthumbnailctrl.h      |  8 +++
 4 files changed, 90 insertions(+), 25 deletions(-)

diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp
index 62180bb066f..68581d04ebd 100644
--- a/indra/newview/llinventorygallery.cpp
+++ b/indra/newview/llinventorygallery.cpp
@@ -58,6 +58,7 @@
 static LLPanelInjector<LLInventoryGallery> t_inventory_gallery("inventory_gallery");
 
 const S32 GALLERY_ITEMS_PER_ROW_MIN = 2;
+const S32 FAST_LOAD_THUMBNAIL_TRSHOLD = 50; // load folders below this value immediately
 
 // Helper dnd functions
 BOOL dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat, BOOL drop, std::string& tooltip_msg, BOOL is_link);
@@ -106,6 +107,7 @@ LLInventoryGallery::LLInventoryGallery(const LLInventoryGallery::Params& p)
       mGalleryWidthFactor(p.gallery_width_factor),
       mIsInitialized(false),
       mRootDirty(false),
+      mLoadThumbnailsImmediately(true),
       mNeedsArrange(false),
       mSearchType(LLInventoryFilter::SEARCHTYPE_NAME),
       mSortOrder(LLInventoryFilter::SO_DATE)
@@ -540,6 +542,12 @@ void LLInventoryGallery::addToGallery(LLInventoryGalleryItem* item)
     int n_prev = n - 1;
     int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1;
 
+    // Avoid loading too many items.
+    // Intent is for small folders to display all content fast
+    // and for large folders to load content mostly as needed
+    // Todo: ideally needs to unload images outside visible area
+    mLoadThumbnailsImmediately = mItemsAddedCount < FAST_LOAD_THUMBNAIL_TRSHOLD;
+
     bool add_row = row_count != row_count_prev;
     int pos = 0;
     if (add_row)
@@ -573,6 +581,8 @@ void LLInventoryGallery::removeFromGalleryLast(LLInventoryGalleryItem* item, boo
     mItemsAddedCount--;
     mIndexToItemMap.erase(mItemsAddedCount);
 
+    mLoadThumbnailsImmediately = mItemsAddedCount < FAST_LOAD_THUMBNAIL_TRSHOLD;
+
     bool remove_row = row_count != row_count_prev;
     removeFromLastRow(mItems[mItemsAddedCount]);
     mItems.pop_back();
@@ -636,6 +646,7 @@ LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, L
     gitem->setUUID(item_id);
     gitem->setGallery(this);
     gitem->setType(type, inventory_type, flags, is_link);
+    gitem->setLoadImmediately(mLoadThumbnailsImmediately);
     gitem->setThumbnail(thumbnail_id);
     gitem->setWorn(is_worn);
     gitem->setCreatorName(get_searchable_creator_name(&gInventory, item_id));
@@ -997,6 +1008,7 @@ void LLInventoryGallery::updateItemThumbnail(LLUUID item_id)
 
     if (mItemMap[item_id])
     {
+        mItemMap[item_id]->setLoadImmediately(mLoadThumbnailsImmediately);
         mItemMap[item_id]->setThumbnail(thumbnail_id);
 
         bool passes_filter = checkAgainstFilters(mItemMap[item_id], mFilterSubString);
@@ -2557,6 +2569,7 @@ BOOL LLInventoryGalleryItem::postBuild()
 {
     mNameText = getChild<LLTextBox>("item_name");
     mTextBgPanel = getChild<LLPanel>("text_bg_panel");
+    mThumbnailCtrl = getChild<LLThumbnailCtrl>("preview_thumbnail");
 
     return TRUE;
 }
@@ -2632,14 +2645,19 @@ void LLInventoryGalleryItem::setThumbnail(LLUUID id)
     mDefaultImage = id.isNull();
     if(mDefaultImage)
     {
-        getChild<LLThumbnailCtrl>("preview_thumbnail")->clearTexture();
+        mThumbnailCtrl->clearTexture();
     }
     else
     {
-        getChild<LLThumbnailCtrl>("preview_thumbnail")->setValue(id);
+        mThumbnailCtrl->setValue(id);
     }
 }
 
+void LLInventoryGalleryItem::setLoadImmediately(bool val)
+{
+    mThumbnailCtrl->setInitImmediately(val);
+}
+
 void LLInventoryGalleryItem::draw()
 {
     if (isFadeItem())
@@ -2654,7 +2672,7 @@ void LLInventoryGalleryItem::draw()
 
         // Draw border
         LLUIColor border_color = LLUIColorTable::instance().getColor(mSelected ? "MenuItemHighlightBgColor" : "TextFgTentativeColor", LLColor4::white);
-        LLRect border = getChildView("preview_thumbnail")->getRect();
+        LLRect border = mThumbnailCtrl->getRect();
         border.mRight = border.mRight + 1;
         border.mTop = border.mTop + 1;
         gl_rect_2d(border, border_color.get(), FALSE);
@@ -2876,7 +2894,7 @@ void LLInventoryGalleryItem::updateNameText()
     mNameText->setFont(getTextFont());
     mNameText->setText(mItemName + mPermSuffix + mWornSuffix);
     mNameText->setToolTip(mItemName + mPermSuffix + mWornSuffix);
-    getChild<LLThumbnailCtrl>("preview_thumbnail")->setToolTip(mItemName + mPermSuffix + mWornSuffix);
+    mThumbnailCtrl->setToolTip(mItemName + mPermSuffix + mWornSuffix);
 }
 
 bool LLInventoryGalleryItem::isFadeItem()
diff --git a/indra/newview/llinventorygallery.h b/indra/newview/llinventorygallery.h
index 9b3f12701fe..0c52e7b7132 100644
--- a/indra/newview/llinventorygallery.h
+++ b/indra/newview/llinventorygallery.h
@@ -39,6 +39,7 @@ class LLInventoryGalleryItem;
 class LLScrollContainer;
 class LLTextBox;
 class LLThumbnailsObserver;
+class LLThumbnailCtrl;
 class LLGalleryGestureObserver;
 
 class LLInventoryGalleryContextMenu;
@@ -246,6 +247,7 @@ class LLInventoryGallery : public LLPanel, public LLEditMenuHandler
     int mRowCount;
     int mItemsAddedCount;
     bool mGalleryCreated;
+    bool mLoadThumbnailsImmediately;
     bool mNeedsArrange;
 
     /* Params */
@@ -342,6 +344,7 @@ class LLInventoryGalleryItem : public LLPanel
     LLAssetType::EType getAssetType() { return mType; }
     void setThumbnail(LLUUID id);
     void setGallery(LLInventoryGallery* gallery) { mGallery = gallery; }
+    void setLoadImmediately(bool val);
     bool isFolder() { return mIsFolder; }
     bool isLink() { return mIsLink; }
     EInventorySortGroup getSortGroup() { return mSortGroup; }
@@ -354,6 +357,7 @@ class LLInventoryGalleryItem : public LLPanel
     LLUUID mUUID;
     LLTextBox* mNameText;
     LLPanel* mTextBgPanel;
+    LLThumbnailCtrl* mThumbnailCtrl;
     bool     mSelected;
     bool     mWorn;
     bool     mDefaultImage;
diff --git a/indra/newview/llthumbnailctrl.cpp b/indra/newview/llthumbnailctrl.cpp
index 72818cf991d..78204204af1 100644
--- a/indra/newview/llthumbnailctrl.cpp
+++ b/indra/newview/llthumbnailctrl.cpp
@@ -57,6 +57,8 @@ LLThumbnailCtrl::LLThumbnailCtrl(const LLThumbnailCtrl::Params& p)
 ,   mFallbackImagep(p.fallback_image)
 ,   mInteractable(p.interactable())
 ,   mShowLoadingPlaceholder(p.show_loading())
+,   mInited(false)
+,   mInitImmediately(true)
 {
     mLoadingPlaceholderString = LLTrans::getString("texture_loading");
     
@@ -83,6 +85,10 @@ LLThumbnailCtrl::~LLThumbnailCtrl()
 
 void LLThumbnailCtrl::draw()
 {
+    if (!mInited)
+    {
+        initImage();
+    }
     LLRect draw_rect = getLocalRect();
     
     if (mBorderVisible)
@@ -170,11 +176,19 @@ void LLThumbnailCtrl::draw()
     LLUICtrl::draw();
 }
 
+void LLThumbnailCtrl::setVisible(BOOL visible)
+{
+    if (!visible && mInited)
+    {
+        unloadImage();
+    }
+    LLUICtrl::setVisible(visible);
+}
+
 void LLThumbnailCtrl::clearTexture()
 {
-    mImageAssetID = LLUUID::null;
-    mTexturep = nullptr;
-    mImagep = nullptr;
+    setValue(LLSD());
+    mInited = true; // nothing to do
 }
 
 // virtual
@@ -190,33 +204,56 @@ void LLThumbnailCtrl::setValue(const LLSD& value)
     
 	LLUICtrl::setValue(tvalue);
     
-    mImageAssetID = LLUUID::null;
-    mTexturep = nullptr;
-    mImagep = nullptr;
-    
-	if (tvalue.isUUID())
-	{
+    unloadImage();
+
+    if (mInitImmediately)
+    {
+        initImage();
+    }
+}
+
+BOOL LLThumbnailCtrl::handleHover(S32 x, S32 y, MASK mask)
+{
+    if (mInteractable && getEnabled())
+    {
+        getWindow()->setCursor(UI_CURSOR_HAND);
+        return TRUE;
+    }
+    return LLUICtrl::handleHover(x, y, mask);
+}
+
+void LLThumbnailCtrl::initImage()
+{
+    if (mInited)
+    {
+        return;
+    }
+    mInited = true;
+    LLSD tvalue = getValue();
+
+    if (tvalue.isUUID())
+    {
         mImageAssetID = tvalue.asUUID();
         if (mImageAssetID.notNull())
         {
             // Should it support baked textures?
             mTexturep = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, MIPMAP_YES, LLGLTexture::BOOST_THUMBNAIL);
-            
+
             mTexturep->forceToSaveRawImage(0);
-            
+
             S32 desired_draw_width = mTexturep->getWidth();
             S32 desired_draw_height = mTexturep->getHeight();
-            
+
             mTexturep->setKnownDrawSize(desired_draw_width, desired_draw_height);
         }
-	}
+    }
     else if (tvalue.isString())
     {
         mImagep = LLUI::getUIImage(tvalue.asString(), LLGLTexture::BOOST_UI);
         if (mImagep)
         {
             LLViewerFetchedTexture* texture = dynamic_cast<LLViewerFetchedTexture*>(mImagep->getImage().get());
-            if(texture)
+            if (texture)
             {
                 mImageAssetID = texture->getID();
             }
@@ -224,14 +261,12 @@ void LLThumbnailCtrl::setValue(const LLSD& value)
     }
 }
 
-BOOL LLThumbnailCtrl::handleHover(S32 x, S32 y, MASK mask)
+void LLThumbnailCtrl::unloadImage()
 {
-    if (mInteractable && getEnabled())
-    {
-        getWindow()->setCursor(UI_CURSOR_HAND);
-        return TRUE;
-    }
-    return LLUICtrl::handleHover(x, y, mask);
+    mImageAssetID = LLUUID::null;
+    mTexturep = nullptr;
+    mImagep = nullptr;
+    mInited = false;
 }
 
 
diff --git a/indra/newview/llthumbnailctrl.h b/indra/newview/llthumbnailctrl.h
index 70f56c429cf..f84a5832715 100644
--- a/indra/newview/llthumbnailctrl.h
+++ b/indra/newview/llthumbnailctrl.h
@@ -64,16 +64,24 @@ class LLThumbnailCtrl
 	virtual ~LLThumbnailCtrl();
 
 	virtual void draw() override;
+    void setVisible(BOOL visible) override;
 
 	virtual void setValue(const LLSD& value ) override;
+    void setInitImmediately(bool val) { mInitImmediately = val; }
     void clearTexture();
     
     virtual BOOL handleHover(S32 x, S32 y, MASK mask) override;
+
+protected:
+    void initImage();
+    void unloadImage();
 	
 private:
     bool mBorderVisible;
     bool mInteractable;
     bool mShowLoadingPlaceholder;
+    bool mInited;
+    bool mInitImmediately;
     std::string mLoadingPlaceholderString;
     LLUUID mImageAssetID;
     LLViewBorder* mBorder;
-- 
GitLab