diff options
| author | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2026-07-20 22:11:01 +0300 |
|---|---|---|
| committer | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2026-07-21 22:42:36 +0300 |
| commit | 4474d4f3fe115cec6b85dbb7f691da8d649fc258 (patch) | |
| tree | c1836166c50e99720f39e9b4d9826e843145266b /indra/newview/llinventorymodel.cpp | |
| parent | 0f0e7e85076df6c71ea23b4bae2b0ed264e52291 (diff) | |
#6014 Make inventory caching flat instead of recursive
Diffstat (limited to 'indra/newview/llinventorymodel.cpp')
| -rw-r--r-- | indra/newview/llinventorymodel.cpp | 167 |
1 files changed, 110 insertions, 57 deletions
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index f85d0eebae..e56db88cf9 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -28,6 +28,7 @@ #include <typeinfo> #include <random> +#include <thread> #include "llinventorymodel.h" @@ -82,7 +83,7 @@ const S32 LLInventoryModel::sCurrentInvCacheVersion = 5; bool LLInventoryModel::sFirstTimeInViewer2 = true; S32 LLInventoryModel::sPendingSystemFolders = 0; -std::vector<std::thread> LLInventoryModel::sPendingCacheThreads; +static std::vector<std::thread> sPendingCacheThreads; ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs @@ -101,45 +102,6 @@ struct InventoryIDPtrLess } }; -class LLCanCache : public LLInventoryCollectFunctor -{ -public: - LLCanCache(LLInventoryModel* model) : mModel(model) {} - virtual ~LLCanCache() {} - virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); -protected: - LLInventoryModel* mModel; - std::set<LLUUID> mCachedCatIDs; -}; - -bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - bool rv = false; - if(item) - { - if(mCachedCatIDs.find(item->getParentUUID()) != mCachedCatIDs.end()) - { - rv = true; - } - } - else if(cat) - { - // HACK: downcast - LLViewerInventoryCategory* c = (LLViewerInventoryCategory*)cat; - if(c->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) - { - S32 descendents_server = c->getDescendentCount(); - S32 descendents_actual = c->getViewerDescendentCount(); - if(descendents_server == descendents_actual) - { - mCachedCatIDs.insert(c->getUUID()); - rv = true; - } - } - } - return rv; -} - struct InventoryCallbackInfo { InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) : @@ -2372,20 +2334,98 @@ void LLInventoryModel::cache( LL_PROFILE_ZONE_SCOPED; LL_DEBUGS(LOG_INV) << "Caching " << parent_folder_id << " for " << agent_id << LL_ENDL; + LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id); - if(!root_cat) return; + if (!root_cat) + { + LL_WARNS(LOG_INV) << "Root category not found for " << parent_folder_id << LL_ENDL; + return; + } + cat_array_t categories; categories.push_back(root_cat); item_array_t items; - LLCanCache can_cache(this); - can_cache(root_cat, NULL); - collectDescendentsIf( - parent_folder_id, - categories, - items, - INCLUDE_TRASH, - can_cache); + // Lambda to check if a category should be cached + // Only cache if it has known version and matching descendent counts + auto should_cache_category = [](LLViewerInventoryCategory* cat) -> bool { + if (!cat || cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + return false; + } + S32 descendents_server = cat->getDescendentCount(); + S32 descendents_actual = cat->getViewerDescendentCount(); + return (descendents_server == descendents_actual); + }; + + // Track which folders we've verified as cacheable descendants + std::unordered_set<LLUUID> processed_folders; + processed_folders.insert(parent_folder_id); + + // First pass: identify all cacheable descendant folders + // Use pair of (folder_id, should_save_children) + std::deque<std::pair<LLUUID, bool>> folders_to_check; + folders_to_check.push_back(std::make_pair(parent_folder_id, should_cache_category(root_cat))); + + while (!folders_to_check.empty()) + { + auto [current_id, save_children] = folders_to_check.front(); + folders_to_check.pop_front(); + + if (save_children) // else incorrect count or version + { + auto item_it = mParentChildItemTree.find(current_id); + if (item_it != mParentChildItemTree.end() && item_it->second) + { + for (LLViewerInventoryItem* item : *(item_it->second)) + { + if (item) + { + items.push_back(item); + } + } + } + } + + // Get child categories directly from the parent-child tree + auto cat_it = mParentChildCategoryTree.find(current_id); + if (cat_it != mParentChildCategoryTree.end() && cat_it->second) + { + for (LLViewerInventoryCategory* child_cat : *(cat_it->second)) + { + if (!child_cat) + { + continue; + } + + // Verify ownership matches (library vs agent inventory) + if (child_cat->getOwnerID() != root_cat->getOwnerID()) + { + LL_WARNS(LOG_INV) << "Owner mismatch in category tree: expected " + << root_cat->getOwnerID() << " got " + << child_cat->getOwnerID() << " for category " + << child_cat->getName() << LL_ENDL; + continue; + } + + const LLUUID& child_id = child_cat->getUUID(); + + // Only process each folder once + if (processed_folders.insert(child_id).second) + { + if (should_cache_category(child_cat)) + { + categories.push_back(child_cat); + folders_to_check.push_back(std::make_pair(child_id, true)); + } + else + { + folders_to_check.push_back(std::make_pair(child_id, false)); + } + } + } + } + } if (categories.empty() && items.empty()) { @@ -2405,10 +2445,15 @@ void LLInventoryModel::cache( std::string gzip_filename = getInvCacheAddres(agent_id); gzip_filename.append(".gz"); - // Launch detached packing thread + if (sPendingCacheThreads.empty()) + { + LL_INFOS(LOG_INV) << "Inventory cache compression started" << LL_ENDL; + } + + // Launch background packing thread // Main thread is the only one modifying sPendingCacheThreads - sPendingCacheThreads.emplace_back( - [temp_file, gzip_filename]() { + auto compress_cache = [temp_file, gzip_filename]() + { LLTimer gzip_timer; if (gzip_file(temp_file, gzip_filename)) @@ -2416,7 +2461,7 @@ void LLInventoryModel::cache( F32 gzip_time = gzip_timer.getElapsedTimeF32(); LL_DEBUGS(LOG_INV) << "Successfully compressed " << temp_file << " to " << gzip_filename - << " in " << gzip_time << "s (async)" << LL_ENDL; + << " in " << gzip_time << "s" << LL_ENDL; LLFile::remove(temp_file); } else @@ -2424,7 +2469,17 @@ void LLInventoryModel::cache( LL_WARNS(LOG_INV) << "Unable to compress " << temp_file << " into " << gzip_filename << LL_ENDL; } - }); + }; + + try + { + sPendingCacheThreads.emplace_back(compress_cache); + } + catch (...) + { + LL_WARNS(LOG_INV) << "Failed to start inventory cache compression thread; running compression synchronously" << LL_ENDL; + compress_cache(); + } } void LLInventoryModel::waitForPendingCacheWrites() @@ -2449,9 +2504,7 @@ void LLInventoryModel::waitForPendingCacheWrites() } } - F32 wait_time = wait_timer.getElapsedTimeF32(); - LL_INFOS(LOG_INV) << "Inventory cache compressions completed in " - << wait_time << "s" << LL_ENDL; + LL_INFOS(LOG_INV) << "Inventory cache compression completed" << LL_ENDL; } } |
