SL-13182 Split buildNewViews over frames

master
Andrey Kleshchev 2021-03-24 01:53:45 +02:00
parent d83c6563aa
commit 7ddbf118f5
8 changed files with 219 additions and 38 deletions

View File

@ -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);
}

View File

@ -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 );

View File

@ -88,6 +88,7 @@ LLConversationViewSession::LLConversationViewSession(const LLConversationViewSes
mFlashStarted(false)
{
mFlashTimer = new LLFlashTimer();
mAreChildrenInited = true; // inventory only
}
LLConversationViewSession::~LLConversationViewSession()

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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;
};

View File

@ -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)
{