diff options
| author | Cosmic Linden <cosmic@lindenlab.com> | 2026-01-02 21:34:34 -0500 |
|---|---|---|
| committer | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2026-01-05 20:45:57 +0200 |
| commit | 4f70d8c830c6213bafcbf40f5ff7eb72840739cc (patch) | |
| tree | edc9d8f247c1de858ef5c1d3c2e7eb26c1661af1 /indra/newview | |
| parent | 11e16d5ae73ec7dd0a738cb5ee2a8d2457176409 (diff) | |
secondlife/viewer#2462: Optimize unloading of prims
Signed-off-by: Rye <rye@alchemyviewer.org>
Diffstat (limited to 'indra/newview')
| -rw-r--r-- | indra/newview/llmeshrepository.cpp | 81 | ||||
| -rw-r--r-- | indra/newview/llmeshrepository.h | 7 | ||||
| -rw-r--r-- | indra/newview/llviewerobjectlist.cpp | 3 | ||||
| -rw-r--r-- | indra/newview/llvovolume.cpp | 27 | ||||
| -rw-r--r-- | indra/newview/llvovolume.h | 1 |
5 files changed, 114 insertions, 5 deletions
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index c0b1a5326a..c3513229c6 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -4271,20 +4271,95 @@ S32 LLMeshRepository::update() return static_cast<S32>(size); } -void LLMeshRepository::unregisterMesh(LLVOVolume* vobj) +#ifdef SHOW_ASSERT +// Brute-force remove the object from all loading queues. Returns true if +// something was removed. +// This function is used in a debug assert to ensure unregisterMesh and +// unregisterSkinInfo are called as intended. +// *TODO: Consider removing at some point if we feel confident about the code +// working as intended. +bool LLMeshRepository::forceUnregisterMesh(LLVOVolume* vobj) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + + bool found = false; + for (auto& lod : mLoadingMeshes) { for (auto& param : lod) { - vector_replace_with_last(param.second.mVolumes, vobj); + llassert(std::find(param.second.mVolumes.begin(), param.second.mVolumes.end(), vobj) == param.second.mVolumes.end()); + found = found || vector_replace_with_last(param.second.mVolumes, vobj); } } for (auto& skin_pair : mLoadingSkins) { - vector_replace_with_last(skin_pair.second.mVolumes, vobj); + llassert(std::find(skin_pair.mVolumes.second.begin(), skin_pair.mVolumes.second.end(), vobj) == skin_pair.mVolumes.second.end()); + found = found || vector_replace_with_last(skin_pair.second.mVolumes, vobj); + } + + return found; +} +#endif + +void LLMeshRepository::unregisterMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + + llassert((mesh_params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH); + llassert(mesh_params.getSculptID().notNull()); + auto& lod = mLoadingMeshes[detail]; + auto param_iter = lod.find(mesh_params.getSculptID()); + if (param_iter != lod.end()) + { + vector_replace_with_last(param_iter->second.mVolumes, vobj); + llassert(!vector_replace_with_last(param_iter->second.mVolumes, vobj)); + if (param_iter->second.mVolumes.empty()) + { + lod.erase(param_iter); + } + } +} + +void LLMeshRepository::unregisterSkinInfo(const LLUUID& mesh_id, LLVOVolume* vobj) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + + llassert(mesh_id.notNull()); + auto skin_pair_iter = mLoadingSkins.find(mesh_id); + if (skin_pair_iter != mLoadingSkins.end()) + { + vector_replace_with_last(skin_pair_iter->second.mVolumes, vobj); + llassert(!vector_replace_with_last(skin_pair_iter->second.mVolumes, vobj)); + if (skin_pair_iter->second.mVolumes.empty()) + { + mLoadingSkins.erase(skin_pair_iter); + } + } +} + +// Lots of dead objects make expensive calls to +// LLMeshRepository::unregisterMesh which may delay shutdown. Avoid this by +// preemptively unregistering all meshes. +// We can also do this safely if all objects are confirmed dead for some other +// reason. +void LLMeshRepository::unregisterAllMeshes() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; + + // The size of mLoadingMeshes and mLoadingSkins may be large and thus + // expensive to iterate over in LLVOVolume::~LLVOVolume. + // This is unnecessary during shutdown, so we ignore the referenced objects in the + // least expensive way which is still safe: by clearing these containers. + // Clear now and not in LLMeshRepository::shutdown because + // LLMeshRepository::notifyLoadedMeshes could (depending on invocation + // order) reference a pointer to an object after it has been deleted. + for (auto& lod : mLoadingMeshes) + { + lod.clear(); } + mLoadingSkins.clear(); } S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 new_lod, S32 last_lod) diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 01b51e753e..886ef70a07 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -862,10 +862,15 @@ public: LLMeshRepository(); void init(); + void unregisterAllMeshes(); void shutdown(); S32 update(); - void unregisterMesh(LLVOVolume* volume); +#ifdef SHOW_ASSERT + bool forceUnregisterMesh(LLVOVolume* volume); +#endif + void unregisterMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail); + void unregisterSkinInfo(const LLUUID& mesh_id, LLVOVolume* vobj); //mesh management functions S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 new_lod = 0, S32 last_lod = -1); diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 1b38fed3bb..50edb408c7 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -65,6 +65,7 @@ #include "lltoolmgr.h" #include "lltoolpie.h" #include "llkeyboard.h" +#include "llmeshrepository.h" #include "u64.h" #include "llviewertexturelist.h" #include "lldatapacker.h" @@ -1398,6 +1399,8 @@ void LLViewerObjectList::killAllObjects() llassert((objectp == gAgentAvatarp) || objectp->isDead()); } + gMeshRepo.unregisterAllMeshes(); + cleanDeadObjects(false); if(!mObjects.empty()) diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index d1318018c9..c68ed01cc1 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -250,10 +250,13 @@ LLVOVolume::~LLVOVolume() delete mVolumeImpl; mVolumeImpl = NULL; - gMeshRepo.unregisterMesh(this); + unregisterOldMeshAndSkin(); + llassert(!gMeshRepo.forceUnregisterMesh(this)); if(!mMediaImplList.empty()) { + LL_PROFILE_ZONE_NAMED_CATEGORY_MEDIA("delete volume media list"); + for(U32 i = 0 ; i < mMediaImplList.size() ; i++) { if(mMediaImplList[i].notNull()) @@ -1048,6 +1051,28 @@ LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline) return mDrawable; } +// Inverse of gMeshRepo.loadMesh and gMeshRepo.getSkinInfo, combined into one function +// Assume a Collada mesh never changes after being set. +void LLVOVolume::unregisterOldMeshAndSkin() +{ + if (mVolumep) + { + const LLVolumeParams& params = mVolumep->getParams(); + if ((params.getSculptType() & LL_SCULPT_TYPE_MASK) == LL_SCULPT_TYPE_MESH) + { + // object is being deleted, so it will no longer need to request + // meshes. + for (S32 lod = 0; lod != LLVolumeLODGroup::NUM_LODS; ++lod) + { + gMeshRepo.unregisterMesh(this, params, lod); + } + // This volume may or may not have a skin + gMeshRepo.unregisterSkinInfo(params.getSculptID(), this); + } + } +} + + bool LLVOVolume::setVolume(const LLVolumeParams ¶ms_in, const S32 detail, bool unique_volume) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 48f999c5d3..b6044bc319 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -227,6 +227,7 @@ public: void setTexture(const S32 face); S32 getIndexInTex(U32 ch) const {return mIndexInTex[ch];} + void unregisterOldMeshAndSkin(); /*virtual*/ bool setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume = false) override; void updateSculptTexture(); void setIndexInTex(U32 ch, S32 index) { mIndexInTex[ch] = index ;} |
