diff options
| author | Andrey Kleshchev <andreykproductengine@lindenlab.com> | 2021-03-24 01:53:45 +0200 | 
|---|---|---|
| committer | Andrey Kleshchev <andreykproductengine@lindenlab.com> | 2021-04-01 23:06:44 +0300 | 
| commit | 7ddbf118f5d1fde24c10b93432209fbe44d91345 (patch) | |
| tree | b3e618ec092310a9b51363606e1e5ec430cd5c42 | |
| parent | d83c6563aacb0cbc2a196d575f409cc89bd8db12 (diff) | |
SL-13182 Split buildNewViews over frames
| -rw-r--r-- | indra/llui/llfolderviewitem.cpp | 14 | ||||
| -rw-r--r-- | indra/llui/llfolderviewitem.h | 15 | ||||
| -rw-r--r-- | indra/newview/llconversationview.cpp | 1 | ||||
| -rw-r--r-- | indra/newview/llfolderviewmodelinventory.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/llinventorybridge.cpp | 19 | ||||
| -rw-r--r-- | indra/newview/llinventorypanel.cpp | 182 | ||||
| -rw-r--r-- | indra/newview/llinventorypanel.h | 17 | ||||
| -rw-r--r-- | indra/newview/llsidepanelinventory.cpp | 7 | 
8 files changed, 219 insertions, 38 deletions
| diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index 1c6c7b1b35..f4a6053b57 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -132,7 +132,6 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)  	mCutGeneration(0),  	mLabelStyle( LLFontGL::NORMAL ),  	mHasVisibleChildren(FALSE), -	mIsFolderComplete(true),      mLocalIndentation(p.folder_indentation),  	mIndentation(0),  	mItemHeight(p.item_height), @@ -1002,11 +1001,11 @@ LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ):  	mCurHeight(0.f),  	mTargetHeight(0.f),  	mAutoOpenCountdown(0.f), +	mIsFolderComplete(false), // folder might have children that are not loaded yet. +	mAreChildrenInited(false), // folder might have children that are not built yet.  	mLastArrangeGeneration( -1 ),  	mLastCalculatedWidth(0)  { -	// folder might have children that are not loaded yet. Mark it as incomplete until chance to check it. -	mIsFolderComplete = false;  }  void LLFolderViewFolder::updateLabelRotation() @@ -1062,13 +1061,16 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height )  {  	// Sort before laying out contents      // Note that we sort from the root (CHUI-849) -	getRoot()->getFolderViewModel()->sort(this); +    if (mAreChildrenInited) +    { +        getRoot()->getFolderViewModel()->sort(this); +    }  	LL_RECORD_BLOCK_TIME(FTM_ARRANGE);  	// evaluate mHasVisibleChildren  	mHasVisibleChildren = false; -	if (getViewModelItem()->descendantsPassedFilter()) +	if (mAreChildrenInited && getViewModelItem()->descendantsPassedFilter())  	{  		// We have to verify that there's at least one child that's not filtered out  		bool found = false; @@ -1094,7 +1096,7 @@ S32 LLFolderViewFolder::arrange( S32* width, S32* height )  		mHasVisibleChildren = found;  	} -	if (!mIsFolderComplete) +	if (!mIsFolderComplete && mAreChildrenInited)  	{  		mIsFolderComplete = getFolderViewModel()->isFolderComplete(this);  	} diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index da09d139e9..9df58d4478 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -116,7 +116,6 @@ protected:  	F32							mControlLabelRotation;  	LLFolderView*				mRoot;  	bool						mHasVisibleChildren, -								mIsFolderComplete, // indicates that some children were not loaded/added yet  								mIsCurSelection,  								mDragAndDropTarget,  								mIsMouseOverTitle, @@ -219,7 +218,10 @@ public:  	BOOL hasVisibleChildren() { return mHasVisibleChildren; }  	// true if object can't have children -	BOOL isFolderComplete() { return mIsFolderComplete; } +	virtual bool isFolderComplete() { return true; } +    // true if object can't have children +    virtual bool areChildrenInited() { return true; } +    virtual void setChildrenInited(bool inited) { }  	// Call through to the viewed object and return true if it can be  	// removed. Returns true if it's removed. @@ -334,6 +336,8 @@ protected:  	S32			mLastArrangeGeneration;  	S32			mLastCalculatedWidth;  	bool		mNeedsSort; +	bool		mIsFolderComplete; // indicates that some children were not loaded/added yet +	bool		mAreChildrenInited; // indicates that no children were initialized  public:  	typedef enum e_recurse_type @@ -385,6 +389,13 @@ public:  	// destroys this folder, and all children  	virtual void destroyView(); +    // whether known children are fully loaded (arrange sets to true) +    virtual bool isFolderComplete() { return mIsFolderComplete; } + +    // whether known children are fully built +    virtual bool areChildrenInited() { return mAreChildrenInited; } +    virtual void setChildrenInited(bool inited) { mAreChildrenInited = inited; } +  	// extractItem() removes the specified item from the folder, but  	// doesn't delete it.  	virtual void extractItem( LLFolderViewItem* item ); diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index 093e772abe..46a0a0b37a 100644 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -88,6 +88,7 @@ LLConversationViewSession::LLConversationViewSession(const LLConversationViewSes  	mFlashStarted(false)  {  	mFlashTimer = new LLFlashTimer(); +	mAreChildrenInited = true; // inventory only  }  LLConversationViewSession::~LLConversationViewSession() diff --git a/indra/newview/llfolderviewmodelinventory.cpp b/indra/newview/llfolderviewmodelinventory.cpp index d40a7234e2..117e2534cb 100644 --- a/indra/newview/llfolderviewmodelinventory.cpp +++ b/indra/newview/llfolderviewmodelinventory.cpp @@ -66,7 +66,7 @@ void LLFolderViewModelInventory::sort( LLFolderViewFolder* folder )  {  	LL_RECORD_BLOCK_TIME(FTM_INVENTORY_SORT); -	if (!needsSort(folder->getViewModelItem())) return; +	if (!folder->areChildrenInited() || !needsSort(folder->getViewModelItem())) return;  	LLFolderViewModelItemInventory* modelp =   static_cast<LLFolderViewModelItemInventory*>(folder->getViewModelItem());  	if (modelp->getUUID().isNull()) return; diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index d35d8456be..15c3f10436 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -3437,9 +3437,22 @@ void LLFolderBridge::copyOutfitToClipboard()  void LLFolderBridge::openItem()  {  	LL_DEBUGS() << "LLFolderBridge::openItem()" << LL_ENDL; -	LLInventoryModel* model = getInventoryModel(); -	if(!model) return; -	if(mUUID.isNull()) return; + +    LLInventoryPanel* panel = mInventoryPanel.get(); +    if (!panel) +    { +        return; +    } +    LLInventoryModel* model = getInventoryModel(); +    if (!model) +    { +        return; +    } +    if (mUUID.isNull()) +    { +        return; +    } +    panel->onFolderOpening(mUUID);  	bool fetching_inventory = model->fetchDescendentsOf(mUUID);  	// Only change folder type if we have the folder contents.  	if (!fetching_inventory) diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 74d9e895c2..09a94c1c09 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -282,17 +282,18 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)  	mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this));  	mInventory->addObserver(mCompletionObserver); -    if (mBuildViewsOnInit) +    if (mBuildViewsOnInit && mViewsInitialized == VIEWS_UNINITIALIZED)      {          // Build view of inventory if we need default full hierarchy and inventory is ready, otherwise do in onIdle.          // Initializing views takes a while so always do it onIdle if viewer already loaded. -        if (mInventory->isInventoryUsable() -            && mViewsInitialized == VIEWS_UNINITIALIZED +        if (mInventory->isInventoryUsable()                          && LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT)          { -            initializeViews(); +            // Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect +            const F64 max_time = 20.f; +            initializeViews(max_time);          } -        else if (mViewsInitialized != VIEWS_INITIALIZING) +        else          {              mViewsInitialized = VIEWS_INITIALIZING;              gIdleCallbacks.addFunction(onIdle, (void*)this); @@ -497,6 +498,23 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve  		view_folder = dynamic_cast<LLFolderViewFolder*>(view_item);  	} +    // if folder is not fully initialized (likely due to delayed load on idle) +    // and we are not rebuilding, try updating children +    if (view_folder +        && !view_folder->areChildrenInited() +        && ( (mask & LLInventoryObserver::REBUILD) == 0)) +    { +        LLInventoryObject const* objectp = mInventory->getObject(item_id); +        if (objectp) +        { +            // Time is low since we only need the item itself and children views. +            // Any value bigger than zero will do to init children, everything else +            // will be processed on idle. +            const F64 max_time = 0.0001f; +            view_item = buildNewViewsWithTimeLimit(item_id, objectp, view_item, max_time); +        } +    } +  	//////////////////////////////  	// LABEL Operation  	// Empty out the display name for relabel. @@ -536,8 +554,13 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve          LLInventoryObject const* objectp = mInventory->getObject(item_id);          if (objectp)          { +            // Time is low since we only need the item itself and children views. +            // Any value bigger than zero will do to init children, everything else +            // will be processed on idle. +            const F64 max_time = 0.0001f; +              // providing NULL directly avoids unnessesary getItemByID calls -            view_item = buildNewViews(item_id, objectp, NULL); +            view_item = buildNewViewsWithTimeLimit(item_id, objectp, NULL, max_time);          }          else          { @@ -589,8 +612,13 @@ void LLInventoryPanel::itemChanged(const LLUUID& item_id, U32 mask, const LLInve              LLInventoryObject const* objectp = mInventory->getObject(item_id);              if (objectp)              { +                // Time is low since we only need the item itself and children views. +                // Any value bigger than zero will do to init children, everything else +                // will be processed on idle. +                const F64 max_time = 0.0001f; +                  // providing NULL directly avoids unnessesary getItemByID calls -                buildNewViews(item_id, objectp, NULL); +                buildNewViewsWithTimeLimit(item_id, objectp, NULL, max_time);              }  			// Select any newly created object that has the auto rename at top of folder root set. @@ -742,12 +770,12 @@ void LLInventoryPanel::onIdle(void *userdata)  		return;  	LLInventoryPanel *self = (LLInventoryPanel*)userdata; -	// Inventory just initialized, do complete build -	if (self->mViewsInitialized != VIEWS_INITIALIZED) +	if (self->mViewsInitialized <= VIEWS_INITIALIZING)  	{ -		self->initializeViews(); +		const F64 max_time = 0.001f; // 1 ms, in this case we need only root folders +		self->initializeViews(max_time); // Shedules LLInventoryPanel::idle()  	} -	if (self->mViewsInitialized == VIEWS_INITIALIZED) +	if (self->mViewsInitialized >= VIEWS_BUILDING)  	{  		gIdleCallbacks.deleteFunction(onIdle, (void*)self);  	} @@ -782,6 +810,49 @@ void LLInventoryPanel::idle(void* user_data)  	} +    bool in_visible_chain = panel->isInVisibleChain(); + +    if (!panel->mBuildViewsQueue.empty()) +    { +        const F64 max_time = in_visible_chain ? 0.006f : 0.001f; // 6 ms +        F64 curent_time = LLTimer::getTotalSeconds(); +        panel->mBuildViewsEndTime = curent_time + max_time; + +        // things added last are closer to root thus of higher priority +        std::deque<LLUUID> priority_list; +        priority_list.swap(panel->mBuildViewsQueue); + +        while (curent_time < panel->mBuildViewsEndTime +            && !priority_list.empty()) +        { +            LLUUID item_id = priority_list.back(); +            priority_list.pop_back(); + +            LLInventoryObject const* objectp = panel->mInventory->getObject(item_id); +            if (objectp && panel->typedViewsFilter(item_id, objectp)) +            { +                LLFolderViewItem* folder_view_item = panel->getItemByID(item_id); +                if (!folder_view_item || !folder_view_item->areChildrenInited()) +                { +                    const LLUUID &parent_id = objectp->getParentUUID(); +                    LLFolderViewFolder* parent_folder = (LLFolderViewFolder*)panel->getItemByID(parent_id); +                    panel->buildViewsTree(item_id, parent_id, objectp, folder_view_item, parent_folder); +                } +            } +            curent_time = LLTimer::getTotalSeconds(); +        } +        while (!priority_list.empty()) +        { +            // items in priority_list are of higher priority +            panel->mBuildViewsQueue.push_back(priority_list.front()); +            priority_list.pop_front(); +        } +        if (panel->mBuildViewsQueue.empty()) +        { +            panel->mViewsInitialized = VIEWS_INITIALIZED; +        } +    } +      // Take into account the fact that the root folder might be invalidated      if (panel->mFolderRoot.get())      { @@ -812,10 +883,16 @@ void LLInventoryPanel::idle(void* user_data)  } -void LLInventoryPanel::initializeViews() +void LLInventoryPanel::initializeViews(F64 max_time)  {  	if (!gInventory.isInventoryUsable()) return; +    mViewsInitialized = VIEWS_BUILDING; + +    F64 curent_time = LLTimer::getTotalSeconds(); +    mBuildViewsEndTime = curent_time + max_time; + +	// init everything  	LLUUID root_id = getRootFolderID();  	if (root_id.notNull())  	{ @@ -823,14 +900,18 @@ void LLInventoryPanel::initializeViews()  	}  	else  	{ -		// Default case: always add "My Inventory" first, "Library" second +		// Default case: always add "My Inventory" root first, "Library" root second +		// If we run out of time, this still should create root folders  		buildNewViews(gInventory.getRootFolderID());		// My Inventory  		buildNewViews(gInventory.getLibraryRootFolderID());	// Library  	} -	gIdleCallbacks.addFunction(idle, this); +    if (mBuildViewsQueue.empty()) +    { +        mViewsInitialized = VIEWS_INITIALIZED; +    } -	mViewsInitialized = VIEWS_INITIALIZED; +	gIdleCallbacks.addFunction(idle, this);  	openStartFolderOrMyInventory(); @@ -930,6 +1011,12 @@ LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id, LLInventoryO      return buildViewsTree(id, parent_id, objectp, folder_view_item, parent_folder);  } +LLFolderViewItem* LLInventoryPanel::buildNewViewsWithTimeLimit(const LLUUID& id, LLInventoryObject const* objectp, LLFolderViewItem *folder_view_item, F64 max_time) +{ +    mBuildViewsEndTime = LLTimer::getTotalSeconds() + max_time; +    return buildNewViews(id, objectp, folder_view_item); +} +  LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,                                                    const LLUUID& parent_id,                                                    LLInventoryObject const* objectp, @@ -1035,9 +1122,33 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,          }  	} +    bool create_children = folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY; + +    if (create_children) +    { +        F64 curent_time = LLTimer::getTotalSeconds(); +        // If function is out of time, we want to shedule it into mBuildViewsQueue +        // If we have time, no matter how little, create views for all children +        // +        // This creates children in 'bulk' to make sure folder has either +        // 'empty and incomplete' or 'complete' states with nothing in between. +        // Folders are marked as mIsFolderComplete == false by default, +        // later arrange() will update mIsFolderComplete by child count +        if (mBuildViewsEndTime < curent_time) +        { +            create_children = false; +            // run it again for the sake of creating children +            mBuildViewsQueue.push_back(id); +        } +        else if (folder_view_item) +        { +            folder_view_item->setChildrenInited(true); +        } +    } +  	// If this is a folder, add the children of the folder and recursively add any   	// child folders. -	if (folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY) +	if (create_children)  	{  		LLViewerInventoryCategory::cat_array_t* categories;  		LLViewerInventoryItem::item_array_t* items; @@ -1053,7 +1164,7 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,  				 ++cat_iter)  			{  				const LLViewerInventoryCategory* cat = (*cat_iter); -                if (typedViewsFilter(cat->getUUID(), cat)) +                if (typedViewsFilter(cat->getUUID(), cat))                   {                      if (has_folders)                      { @@ -1077,17 +1188,16 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id,  				 item_iter != items->end();  				 ++item_iter)  			{ +                // At the moment we have to build folder's items in bulk and ignore mBuildViewsEndTime  				const LLViewerInventoryItem* item = (*item_iter);                  if (typedViewsFilter(item->getUUID(), item))                  { -                      // This can be optimized: we don't need to call getItemByID()                      // each time, especially since content is growing, we can just                      // iter over copy of mItemMap in some way                      LLFolderViewItem* view_itemp = getItemByID(item->getUUID());                      buildViewsTree(item->getUUID(), id, item, view_itemp, parentp);                  } -  			}  		}  		mInventory->unlockDirectDescendentArrays(id); @@ -1200,6 +1310,18 @@ void LLInventoryPanel::onFocusReceived()  	LLPanel::onFocusReceived();  } +void LLInventoryPanel::onFolderOpening(const LLUUID &id) +{ +    LLFolderViewItem* folder = getItemByID(id); +    if (folder && !folder->areChildrenInited()) +    { +        // Last item in list will be processed first. +        // This might result in dupplicates in list, but it +        // isn't critical, views won't be created twice +        mBuildViewsQueue.push_back(id); +    } +} +  bool LLInventoryPanel::addBadge(LLBadge * badge)  {  	bool badge_added = false; @@ -1221,7 +1343,7 @@ void LLInventoryPanel::openAllFolders()  void LLInventoryPanel::setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus)  {  	// Don't select objects in COF (e.g. to prevent refocus when items are worn). -	const LLInventoryObject *obj = gInventory.getObject(obj_id); +	const LLInventoryObject *obj = mInventory->getObject(obj_id);  	if (obj && obj->getParentUUID() == LLAppearanceMgr::instance().getCOF())  	{  		return; @@ -1257,6 +1379,12 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it  		if (view_model)  		{  			LLUUID id = view_model->getUUID(); +            if (!(*it)->areChildrenInited()) +            { +                const F64 max_time = 0.0001f; +                mBuildViewsEndTime = LLTimer::getTotalSeconds() + max_time; +                buildNewViews(id); +            }  			LLViewerInventoryItem* inv_item = mInventory->getItem(id);  			if (inv_item && !inv_item->isFinished()) @@ -1714,6 +1842,20 @@ LLFolderViewFolder* LLInventoryPanel::getFolderByID(const LLUUID& id)  void LLInventoryPanel::setSelectionByID( const LLUUID& obj_id, BOOL    take_keyboard_focus )  {  	LLFolderViewItem* itemp = getItemByID(obj_id); + +    if (itemp && !itemp->areChildrenInited()) +    { +        LLInventoryObject const* objectp = mInventory->getObject(obj_id); +        if (objectp) +        { +            // Time is low since we only need the item itself and children views. +            // Any value bigger than zero will do to init children, everything else +            // will be processed on idle. +            const F64 max_time = 0.0001f; +            buildNewViewsWithTimeLimit(obj_id, objectp, itemp, max_time); +        } +    } +  	if(itemp && itemp->getViewModelItem())  	{  		itemp->arrangeAndSet(TRUE, take_keyboard_focus); diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index ad6010f09c..06547ebf0e 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -171,6 +171,7 @@ public:  	// LLUICtrl methods  	 /*virtual*/ void onFocusLost();  	 /*virtual*/ void onFocusReceived(); +     void onFolderOpening(const LLUUID &id);  	// LLBadgeHolder methods  	bool addBadge(LLBadge * badge); @@ -317,12 +318,9 @@ private:  	//--------------------------------------------------------------------  public:  	void addHideFolderType(LLFolderType::EType folder_type); - -public: -	bool getViewsInitialized() const { return mViewsInitialized == VIEWS_INITIALIZED; }  protected:  	// Builds the UI.  Call this once the inventory is usable. -	void 				initializeViews(); +	void 				initializeViews(F64 max_time);  	// Specific inventory colors  	static bool                 sColorSetInitialized; @@ -330,13 +328,19 @@ protected:  	static LLUIColor			sDefaultHighlightColor;  	static LLUIColor			sLibraryColor;  	static LLUIColor			sLinkColor; -	 + +    // All buildNewViews() expect time limit mBuildViewsEndTime to be set  	LLFolderViewItem*			buildNewViews(const LLUUID& id);      LLFolderViewItem*			buildNewViews(const LLUUID& id,                                                LLInventoryObject const* objectp);      LLFolderViewItem*			buildNewViews(const LLUUID& id,                                                LLInventoryObject const* objectp,                                                LLFolderViewItem *target_view); + +    LLFolderViewItem*			buildNewViewsWithTimeLimit(const LLUUID& id, +                                                           LLInventoryObject const* objectp, +                                                           LLFolderViewItem *folder_view_item, +                                                           F64 max_time);      // if certain types are not allowed, no reason to create views      virtual bool				typedViewsFilter(const LLUUID& id, LLInventoryObject const* objectp) { return true; } @@ -359,11 +363,14 @@ private:      {          VIEWS_UNINITIALIZED = 0,          VIEWS_INITIALIZING, +        VIEWS_BUILDING, // Root folder exists          VIEWS_INITIALIZED,      } EViewsInitializationState;  	bool						mBuildViewsOnInit;      EViewsInitializationState	mViewsInitialized; // Whether views have been generated +    F64							mBuildViewsEndTime; // Stop building views past this timestamp +    std::deque<LLUUID>			mBuildViewsQueue;  }; diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index ea7e649792..a5dcdc41ed 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -653,7 +653,12 @@ bool LLSidepanelInventory::canWearSelected()  LLInventoryItem *LLSidepanelInventory::getSelectedItem()  { -	LLFolderViewItem* current_item = mPanelMainInventory->getActivePanel()->getRootFolder()->getCurSelectedItem(); +    LLFolderView* root = mPanelMainInventory->getActivePanel()->getRootFolder(); +    if (!root) +    { +        return NULL; +    } +	LLFolderViewItem* current_item = root->getCurSelectedItem();  	if (!current_item)  	{ | 
