Skip to content
17 changes: 11 additions & 6 deletions indra/llcommon/llsingleton.h
Original file line number Diff line number Diff line change
Expand Up @@ -522,13 +522,16 @@ class LLSingleton : public LLSingletonBase
case INITIALIZING:
// here if DERIVED_TYPE::initSingleton() (directly or indirectly)
// calls DERIVED_TYPE::getInstance(): go ahead and allow it
case INITIALIZED:
// normal subsequent calls
// record the dependency, if any: check if we got here from another
// record the dependency: check if we got here from another
// LLSingleton's constructor or initSingleton() method
capture_dependency(lk->mInstance);
return lk->mInstance;

case INITIALIZED:
// normal subsequent calls - skip capture_dependency() for performance
// dependencies are only tracked during initialization
return lk->mInstance;

case DELETED:
// called after deleteSingleton()
logwarns({"Trying to access deleted singleton ",
Expand Down Expand Up @@ -728,12 +731,14 @@ class LLParamSingleton : public LLSingleton<DERIVED_TYPE>

case super::INITIALIZING:
// As with LLSingleton, explicitly permit circular calls from
// within initSingleton()
case super::INITIALIZED:
// for any valid call, capture dependencies
// within initSingleton() and capture dependencies
super::capture_dependency(lk->mInstance);
return lk->mInstance;

case super::INITIALIZED:
// normal subsequent calls - skip capture_dependency() for performance
return lk->mInstance;

case super::DELETED:
super::logerrs({"Trying to access deleted param singleton ",
super::template classname<DERIVED_TYPE>()});
Expand Down
18 changes: 18 additions & 0 deletions indra/llinventory/llfoldertype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,24 @@ bool LLFolderType::lookupIsEnsembleType(EType folder_type)
folder_type <= FT_ENSEMBLE_END);
}

// static
bool LLFolderType::lookupIsEssentialType(EType folder_type)
{
if (folder_type == FT_NONE)
{
return false;
}

if (folder_type == FT_ROOT_INVENTORY)
{
return true;
}

// Essential folders are those needed for basic viewer operation:
// singleton system folders and ensemble (outfit) folders
return lookupIsSingletonType(folder_type) || lookupIsEnsembleType(folder_type);
}

// static
LLAssetType::EType LLFolderType::folderTypeToAssetType(LLFolderType::EType folder_type)
{
Expand Down
2 changes: 2 additions & 0 deletions indra/llinventory/llfoldertype.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class LL_COMMON_API LLFolderType
static bool lookupIsAutomaticType(EType folder_type);
static bool lookupIsSingletonType(EType folder_type);
static bool lookupIsEnsembleType(EType folder_type);
// Returns true if this folder type should be fully loaded during async inventory startup
static bool lookupIsEssentialType(EType folder_type);

static LLAssetType::EType folderTypeToAssetType(LLFolderType::EType folder_type);
static LLFolderType::EType assetTypeToFolderType(LLAssetType::EType asset_type);
Expand Down
42 changes: 27 additions & 15 deletions indra/llui/llpanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,58 +489,70 @@ bool LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu
LL_RECORD_BLOCK_TIME(FTM_PANEL_SETUP);

LLXMLNodePtr referenced_xml;
std::string xml_filename = mXMLFilename;
const std::string& xml_filename = mXMLFilename;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holding a reference to mXMLFilename seems pointless as it's only used in the following if-check now.


// if the panel didn't provide a filename, check the node
if (xml_filename.empty())
{
node->getAttributeString("filename", xml_filename);
setXMLFilename(xml_filename);
std::string temp_filename;
node->getAttributeString("filename", temp_filename);
setXMLFilename(temp_filename);
}

// Cache singleton and filename to avoid repeated calls
LLUICtrlFactory* factory = LLUICtrlFactory::getInstance();

// Cache node name pointer to avoid repeated dereferencing
const LLStringTableEntry* node_name = node->getName();

// Cache registry to avoid repeated singleton access
const child_registry_t& registry = child_registry_t::instance();

LLXUIParser parser;

if (!xml_filename.empty())
if (!mXMLFilename.empty())
{
if (output_node)
{
//if we are exporting, we want to export the current xml
//not the referenced xml
parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName());
parser.readXUI(node, params, factory->getCurFileName());
Params output_params(params);
setupParamsForExport(output_params, parent);
output_node->setName(node->getName()->mString);
output_node->setName(node_name->mString);
parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params);
return true;
}

LLUICtrlFactory::instance().pushFileName(xml_filename);
factory->pushFileName(mXMLFilename);

LL_RECORD_BLOCK_TIME(FTM_EXTERNAL_PANEL_LOAD);
if (!LLUICtrlFactory::getLayeredXMLNode(xml_filename, referenced_xml))
if (!LLUICtrlFactory::getLayeredXMLNode(mXMLFilename, referenced_xml))
{
LL_WARNS() << "Couldn't parse panel from: " << xml_filename << LL_ENDL;
LL_WARNS() << "Couldn't parse panel from: " << mXMLFilename << LL_ENDL;

return false;
}

parser.readXUI(referenced_xml, params, LLUICtrlFactory::getInstance()->getCurFileName());
// Get filename after pushFileName
const std::string& updated_filename = factory->getCurFileName();
parser.readXUI(referenced_xml, params, updated_filename);

// add children using dimensions from referenced xml for consistent layout
setShape(params.rect);
LLUICtrlFactory::createChildren(this, referenced_xml, child_registry_t::instance());
LLUICtrlFactory::createChildren(this, referenced_xml, registry);

LLUICtrlFactory::instance().popFileName();
factory->popFileName();
}

// ask LLUICtrlFactory for filename, since xml_filename might be empty
parser.readXUI(node, params, LLUICtrlFactory::getInstance()->getCurFileName());
parser.readXUI(node, params, factory->getCurFileName());

if (output_node)
{
Params output_params(params);
setupParamsForExport(output_params, parent);
output_node->setName(node->getName()->mString);
output_node->setName(node_name->mString);
parser.writeXUI(output_node, output_params, LLInitParam::default_parse_rules(), &default_params);
}

Expand All @@ -552,7 +564,7 @@ bool LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu
}

// add children
LLUICtrlFactory::createChildren(this, node, child_registry_t::instance(), output_node);
LLUICtrlFactory::createChildren(this, node, registry, output_node);

// Connect to parent after children are built, because tab containers
// do a reshape() on their child panels, which requires that the children
Expand Down
23 changes: 23 additions & 0 deletions indra/llui/llview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ bool LLView::addChild(LLView* child, S32 tab_group)
// add to front of child list, as normal
mChildList.push_front(child);

// Add to name cache for fast lookup
if (!child->getName().empty())
{
mChildNameCache[child->getName()] = child;
}

// add to tab order list
if (tab_group != 0)
{
Expand Down Expand Up @@ -344,6 +350,13 @@ void LLView::removeChild(LLView* child)
// if we are removing an item we are currently iterating over, that would be bad
llassert(!child->mInDraw);
mChildList.remove( child );

// Remove from name cache
if (!child->getName().empty())
{
mChildNameCache.erase(child->getName());
}

child->mParentView = NULL;
child_tab_order_t::iterator found = mTabOrder.find(child);
if (found != mTabOrder.end())
Expand Down Expand Up @@ -1649,15 +1662,25 @@ LLView* LLView::findChildView(std::string_view name, bool recurse) const
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_UI;

// Check cache first for direct children - O(1) lookup instead of O(n)
auto cache_it = mChildNameCache.find(name);
if (cache_it != mChildNameCache.end())
{
return cache_it->second;
}

// Look for direct children *first*
for (LLView* childp : mChildList)
{
llassert(childp);
if (childp->getName() == name)
{
// Cache the result for next lookup
mChildNameCache[name] = childp;
return childp;
}
}

if (recurse)
{
// Look inside each child as well.
Expand Down
3 changes: 3 additions & 0 deletions indra/llui/llview.h
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,9 @@ class LLView
LLView* mParentView;
child_list_t mChildList;

// Cache for fast child lookup by name - O(1) instead of O(n)
mutable std::unordered_map<std::string_view, LLView*> mChildNameCache;

// location in pixels, relative to surrounding structure, bottom,left=0,0
bool mVisible;
LLRect mRect;
Expand Down
66 changes: 66 additions & 0 deletions indra/newview/app_settings/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3258,6 +3258,72 @@
<key>Value</key>
<integer>255</integer>
</map>
<key>ForceAsyncInventorySkeleton</key>
<map>
<key>Comment</key>
<string>Force viewer to skip legacy login inventory skeleton and rely on async AIS fetching (QA only).</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>AsyncInventoryMaxConcurrentFetches</key>
<map>
<key>Comment</key>
<string>Maximum number of concurrent AIS fetches used during async inventory skeleton loading.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>2</integer>
</map>
<key>AsyncInventoryNotifyMinInterval</key>
<map>
<key>Comment</key>
<string>Minimum seconds between inventory observer notifications while async inventory loading is active.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>0.05</real>
</map>
<key>AsyncInventoryCapsTimeout</key>
<map>
<key>Comment</key>
<string>Maximum seconds to wait for inventory capabilities during async skeleton loading.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>45.0</real>
</map>
<key>AsyncInventoryFetchTimeout</key>
<map>
<key>Comment</key>
<string>Maximum seconds for the entire async inventory skeleton fetch process.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>180.0</real>
</map>
<key>AsyncInventoryEssentialTimeout</key>
<map>
<key>Comment</key>
<string>Maximum seconds to wait for essential inventory folders during async loading.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>90.0</real>
</map>
<key>ForceLoginURL</key>
<map>
<key>Comment</key>
Expand Down
Loading