Skip to content

Commit c2460e2

Browse files
committed
#4724 Fix performance problems with My Outfits
1 parent 921856d commit c2460e2

File tree

7 files changed

+122
-20
lines changed

7 files changed

+122
-20
lines changed

indra/llui/llaccordionctrl.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,11 @@ void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S3
303303
return;
304304
LLRect panel_rect = panel->getRect();
305305
panel_rect.setLeftTopAndSize( left, top, width, height);
306-
panel->reshape( width, height, 1);
307-
panel->setRect(panel_rect);
306+
if (panel->getRect() != panel_rect)
307+
{
308+
panel->reshape( width, height, 1);
309+
panel->setRect(panel_rect);
310+
}
308311
}
309312

310313
void LLAccordionCtrl::ctrlShiftVertical(LLView* panel, S32 delta)
@@ -494,6 +497,7 @@ void LLAccordionCtrl::arrangeMultiple()
494497

495498
void LLAccordionCtrl::arrange()
496499
{
500+
LL_PROFILE_ZONE_SCOPED;
497501
updateNoTabsHelpTextVisibility();
498502

499503
if (mAccordionTabs.empty())

indra/llui/llaccordionctrltab.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,10 +248,22 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw()
248248
void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, bool called_from_parent /* = true */)
249249
{
250250
S32 header_height = mHeaderTextbox->getTextPixelHeight();
251+
LLRect old_header_rect = mHeaderTextbox->getRect();
251252

252253
LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET, (height + header_height) / 2, width, (height - header_height) / 2);
253-
mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight());
254-
mHeaderTextbox->setRect(textboxRect);
254+
if (old_header_rect.getHeight() != textboxRect.getHeight()
255+
|| old_header_rect.mLeft != textboxRect.mLeft
256+
|| old_header_rect.mTop != textboxRect.mTop
257+
|| old_header_rect.getWidth() > textboxRect.getWidth() // reducing header's width
258+
|| (old_header_rect.getWidth() < textboxRect.getWidth() && old_header_rect.getWidth() < mHeaderTextbox->getTextPixelWidth()))
259+
{
260+
// Expensive text reflow
261+
// Update if position or height changes
262+
// Update if width reduces
263+
// But do not update if text already fits and width increases (arguably LLTextBox::reshape should be smarter, not Accordion)
264+
mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight());
265+
mHeaderTextbox->setRect(textboxRect);
266+
}
255267

256268
if (mHeaderTextbox->getTextPixelWidth() > mHeaderTextbox->getRect().getWidth())
257269
{
@@ -416,8 +428,11 @@ void LLAccordionCtrlTab::reshape(S32 width, S32 height, bool called_from_parent
416428
LLRect headerRect;
417429

418430
headerRect.setLeftTopAndSize(0, height, width, HEADER_HEIGHT);
419-
mHeader->setRect(headerRect);
420-
mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
431+
if (mHeader->getRect() != headerRect)
432+
{
433+
mHeader->setRect(headerRect);
434+
mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
435+
}
421436

422437
if (!mDisplayChildren)
423438
return;
@@ -932,7 +947,7 @@ void LLAccordionCtrlTab::adjustContainerPanel(const LLRect& child_rect)
932947
show_hide_scrollbar(child_rect);
933948
updateLayout(child_rect);
934949
}
935-
else
950+
else if (mContainerPanel->getRect() != child_rect)
936951
{
937952
mContainerPanel->reshape(child_rect.getWidth(), child_rect.getHeight());
938953
mContainerPanel->setRect(child_rect);

indra/llui/lltextbase.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,8 @@ void LLTextBase::reshape(S32 width, S32 height, bool called_from_parent)
14481448
// up-to-date mVisibleTextRect
14491449
updateRects();
14501450

1451+
// Todo: This might be wrong. updateRects already sets needsReflow conditionaly.
1452+
// Reflow is expensive and doing it at any twith can be too much.
14511453
needsReflow();
14521454
}
14531455
}

indra/newview/llinventoryitemslist.cpp

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
#include "llinventorymodel.h"
4141
#include "llviewerinventory.h"
4242

43+
bool LLInventoryItemsList::sListIdleRegistered = false;
44+
LLInventoryItemsList::all_list_t LLInventoryItemsList::sAllLists;
45+
LLInventoryItemsList::all_list_t::iterator LLInventoryItemsList::sAllListIter;
46+
4347
LLInventoryItemsList::Params::Params()
4448
{}
4549

@@ -55,13 +59,39 @@ LLInventoryItemsList::LLInventoryItemsList(const LLInventoryItemsList::Params& p
5559

5660
setNoFilteredItemsMsg(LLTrans::getString("InventoryNoMatchingItems"));
5761

58-
gIdleCallbacks.addFunction(idle, this);
62+
sAllLists.push_back(this);
63+
sAllListIter = sAllLists.begin();
64+
65+
if (!sListIdleRegistered)
66+
{
67+
sAllListIter = sAllLists.begin();
68+
gIdleCallbacks.addFunction(idle, nullptr);
69+
70+
LLEventPumps::instance().obtain("LLApp").listen(
71+
"LLInventoryItemsList",
72+
[](const LLSD& stat)
73+
{
74+
std::string status(stat["status"]);
75+
if (status != "running")
76+
{
77+
// viewer is starting shutdown
78+
gIdleCallbacks.deleteFunction(idle, nullptr);
79+
}
80+
return false;
81+
});
82+
sListIdleRegistered = true;
83+
}
5984
}
6085

6186
// virtual
6287
LLInventoryItemsList::~LLInventoryItemsList()
6388
{
64-
gIdleCallbacks.deleteFunction(idle, this);
89+
auto it = std::find(sAllLists.begin(), sAllLists.end(), this);
90+
if (it != sAllLists.end())
91+
{
92+
sAllLists.erase(it);
93+
sAllListIter = sAllLists.begin();
94+
}
6595
}
6696

6797
void LLInventoryItemsList::refreshList(const LLInventoryModel::item_array_t item_array)
@@ -111,25 +141,55 @@ void LLInventoryItemsList::updateSelection()
111141
mSelectTheseIDs.clear();
112142
}
113143

114-
void LLInventoryItemsList::doIdle()
144+
bool LLInventoryItemsList::doIdle()
115145
{
116-
if (mRefreshState == REFRESH_COMPLETE) return;
146+
if (mRefreshState == REFRESH_COMPLETE) return true; // done
117147

118148
if (isInVisibleChain() || mForceRefresh || !getFilterSubString().empty())
119149
{
120150
refresh();
121151

122152
mRefreshCompleteSignal(this, LLSD());
153+
return false; // keep going
123154
}
155+
return true; // done
124156
}
125157

126158
//static
127159
void LLInventoryItemsList::idle(void* user_data)
128160
{
129-
LLInventoryItemsList* self = static_cast<LLInventoryItemsList*>(user_data);
130-
if ( self )
131-
{ // Do the real idle
132-
self->doIdle();
161+
if (sAllLists.empty())
162+
return;
163+
164+
LL_PROFILE_ZONE_SCOPED;
165+
166+
using namespace std::chrono;
167+
auto start = steady_clock::now();
168+
const milliseconds time_limit = milliseconds(3);
169+
const auto end_time = start + time_limit;
170+
S32 max_update_count = 50;
171+
172+
if (sAllListIter == sAllLists.end())
173+
{
174+
sAllListIter = sAllLists.begin();
175+
}
176+
177+
S32 updated = 0;
178+
while (steady_clock::now() < end_time
179+
&& updated < max_update_count
180+
&& sAllListIter != sAllLists.end())
181+
{
182+
LLInventoryItemsList* list = *sAllListIter;
183+
// Refresh is split into multiple separate parts,
184+
// so keep repeating it while there is time, until done.
185+
// Todo: refresh() split is pointless now?
186+
// Or still useful for large folders?
187+
if (list->doIdle())
188+
{
189+
// Item is done
190+
++sAllListIter;
191+
updated++;
192+
}
133193
}
134194
}
135195

@@ -141,6 +201,7 @@ void LLInventoryItemsList::refresh()
141201
{
142202
case REFRESH_ALL:
143203
{
204+
LL_PROFILE_ZONE_NAMED("items_refresh_all");
144205
mAddedItems.clear();
145206
mRemovedItems.clear();
146207
computeDifference(getIDs(), mAddedItems, mRemovedItems);
@@ -163,6 +224,7 @@ void LLInventoryItemsList::refresh()
163224
}
164225
case REFRESH_LIST_ERASE:
165226
{
227+
LL_PROFILE_ZONE_NAMED("items_refresh_erase");
166228
uuid_vec_t::const_iterator it = mRemovedItems.begin();
167229
for (; mRemovedItems.end() != it; ++it)
168230
{
@@ -175,6 +237,7 @@ void LLInventoryItemsList::refresh()
175237
}
176238
case REFRESH_LIST_APPEND:
177239
{
240+
LL_PROFILE_ZONE_NAMED("items_refresh_append");
178241
static const unsigned ADD_LIMIT = 25; // Note: affects perfomance
179242

180243
unsigned int nadded = 0;
@@ -239,6 +302,7 @@ void LLInventoryItemsList::refresh()
239302
}
240303
case REFRESH_LIST_SORT:
241304
{
305+
LL_PROFILE_ZONE_NAMED("items_refresh_sort");
242306
// Filter, sort, rearrange and notify parent about shape changes
243307
filterItems(true, true);
244308

@@ -255,7 +319,10 @@ void LLInventoryItemsList::refresh()
255319
break;
256320
}
257321
default:
258-
break;
322+
{
323+
mRefreshState = REFRESH_COMPLETE;
324+
break;
325+
}
259326
}
260327

261328
setForceRefresh(mRefreshState != REFRESH_COMPLETE);

indra/newview/llinventoryitemslist.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ class LLInventoryItemsList : public LLFlatListViewEx
5959
* Sets the flag indicating that the list needs to be refreshed even if it is
6060
* not currently visible.
6161
*/
62-
void setForceRefresh(bool force_refresh) { mForceRefresh = force_refresh; }
62+
void setForceRefresh(bool force_refresh)
63+
{
64+
mForceRefresh = force_refresh;
65+
}
6366

6467
/**
6568
* If refreshes when invisible.
@@ -76,7 +79,7 @@ class LLInventoryItemsList : public LLFlatListViewEx
7679
* This is needed for example to filter items of the list hidden by closed
7780
* accordion tab.
7881
*/
79-
virtual void doIdle(); // Real idle routine
82+
bool doIdle(); // Real idle routine
8083
static void idle(void* user_data); // static glue to doIdle()
8184

8285
protected:
@@ -126,6 +129,12 @@ class LLInventoryItemsList : public LLFlatListViewEx
126129
bool mForceRefresh;
127130

128131
commit_signal_t mRefreshCompleteSignal;
132+
133+
// Update synchronization
134+
typedef std::vector<LLInventoryItemsList*> all_list_t;
135+
static all_list_t sAllLists;
136+
static all_list_t::iterator sAllListIter;
137+
static bool sListIdleRegistered;
129138
};
130139

131140
#endif //LL_LLINVENTORYITEMSLIST_H

indra/newview/lloutfitgallery.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,7 @@ void LLOutfitGallery::getCurrentCategories(uuid_vec_t& vcur)
818818

819819
void LLOutfitGallery::updateAddedCategory(LLUUID cat_id)
820820
{
821+
LL_PROFILE_ZONE_SCOPED;
821822
LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
822823
if (!cat) return;
823824

indra/newview/lloutfitslist.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ void LLOutfitsList::onOpen(const LLSD& info)
181181

182182
void LLOutfitsList::updateAddedCategory(LLUUID cat_id)
183183
{
184+
LL_PROFILE_ZONE_SCOPED;
184185
LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id);
185186
if (!cat) return;
186187

@@ -251,7 +252,9 @@ void LLOutfitsList::updateAddedCategory(LLUUID cat_id)
251252

252253
if (AISAPI::isAvailable() && LLInventoryModelBackgroundFetch::instance().folderFetchActive())
253254
{
254-
// for reliability just fetch it whole, linked items included
255+
// For reliability just fetch it whole, linked items included
256+
// Todo: list is not warrantied to exist once callback arrives
257+
// Fix it!
255258
LLInventoryModelBackgroundFetch::instance().fetchFolderAndLinks(cat_id, [cat_id, list]
256259
{
257260
if (list)
@@ -1059,6 +1062,7 @@ void LLOutfitListBase::onIdle(void* userdata)
10591062

10601063
void LLOutfitListBase::onIdleRefreshList()
10611064
{
1065+
LL_PROFILE_ZONE_SCOPED;
10621066
if (LLAppViewer::instance()->quitRequested())
10631067
{
10641068
mRefreshListState.CategoryUUID.setNull();
@@ -1072,7 +1076,7 @@ void LLOutfitListBase::onIdleRefreshList()
10721076
return;
10731077
}
10741078

1075-
const F64 MAX_TIME = 0.05f;
1079+
const F64 MAX_TIME = 0.005f;
10761080
F64 curent_time = LLTimer::getTotalSeconds();
10771081
const F64 end_time = curent_time + MAX_TIME;
10781082

0 commit comments

Comments
 (0)