From 8f274bfdf9683895a2245b531dc45f4d047d69d6 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 11 Nov 2024 20:53:50 +0200 Subject: #1186 Make lod and skin request share priorities skins are needed to decloud avatars as much as lods --- indra/newview/llmeshrepository.cpp | 149 ++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 59 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index a5bed1dbe6..4eb1fdc8de 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -925,7 +925,7 @@ void LLMeshRepoThread::run() } sRequestWaterLevel = static_cast(mHttpRequestSet.size()); // Stats data update - // NOTE: order of queue processing intentionally favors LOD requests over header requests + // NOTE: order of queue processing intentionally favors LOD and Skin requests over header requests // Todo: we are processing mLODReqQ, mHeaderReqQ, mSkinRequests, mDecompositionRequests and mPhysicsShapeRequests // in relatively similar manners, remake code to simplify/unify the process, // like processRequests(&requestQ, fetchFunction); which does same thing for each element @@ -979,6 +979,50 @@ void LLMeshRepoThread::run() } } + if (mHttpRequestSet.size() < sRequestHighWater + && !mSkinRequests.empty()) + { + if (!mSkinRequests.empty()) + { + std::list incomplete; + while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) + { + + mMutex->lock(); + auto req = mSkinRequests.front(); + mSkinRequests.pop_front(); + mMutex->unlock(); + if (req.isDelayed()) + { + incomplete.emplace_back(req); + } + else if (!fetchMeshSkinInfo(req.mId, req.canRetry())) + { + if (req.canRetry()) + { + req.updateTime(); + incomplete.emplace_back(req); + } + else + { + LLMutexLock locker(mMutex); + mSkinUnavailableQ.push_back(req); + LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL; + } + } + } + + if (!incomplete.empty()) + { + LLMutexLock locker(mMutex); + for (const auto& req : incomplete) + { + mSkinRequests.push_back(req); + } + } + } + } + if (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) { std::list incomplete; @@ -1028,54 +1072,13 @@ void LLMeshRepoThread::run() // performing long-duration actions. if (mHttpRequestSet.size() < sRequestHighWater - && (!mSkinRequests.empty() - || !mDecompositionRequests.empty() + && (!mDecompositionRequests.empty() || !mPhysicsShapeRequests.empty())) { // Something to do probably, lock and double-check. We don't want // to hold the lock long here. That will stall main thread activities // so we bounce it. - if (!mSkinRequests.empty()) - { - std::list incomplete; - while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) - { - - mMutex->lock(); - auto req = mSkinRequests.front(); - mSkinRequests.pop_front(); - mMutex->unlock(); - if (req.isDelayed()) - { - incomplete.emplace_back(req); - } - else if (!fetchMeshSkinInfo(req.mId, req.canRetry())) - { - if (req.canRetry()) - { - req.updateTime(); - incomplete.emplace_back(req); - } - else - { - LLMutexLock locker(mMutex); - mSkinUnavailableQ.push_back(req); - LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL; - } - } - } - - if (!incomplete.empty()) - { - LLMutexLock locker(mMutex); - for (const auto& req : incomplete) - { - mSkinRequests.push_back(req); - } - } - } - // holding lock, try next list // *TODO: For UI/debug-oriented lists, we might drop the fine- // grained locking as there's a lowered expectation of smoothness @@ -1195,7 +1198,6 @@ void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 } } - void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { //could be called from any thread const LLUUID& mesh_id = mesh_params.getSculptID(); @@ -3738,7 +3740,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para { //first request for this mesh mLoadingMeshes[detail][mesh_id].push_back(vobj); - mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail)); + mPendingRequests.push_back(PendingRequestLOD(mesh_params, detail)); LLMeshRepository::sLODPending++; } } @@ -4011,35 +4013,64 @@ void LLMeshRepository::notifyLoadedMeshes() score_map[iter->first] = max_score; } } + for (mesh_load_map::iterator iter = mLoadingSkins.begin(); iter != mLoadingSkins.end(); ++iter) + { + F32 max_score = 0.f; + for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) + { + LLVOVolume* object = *obj_iter; + if (object) + { + LLDrawable* drawable = object->mDrawable; + if (drawable) + { + F32 cur_score = drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f); + max_score = llmax(max_score, cur_score); + } + } + } + + score_map[iter->first] = max_score; + } //set "score" for pending requests - for (std::vector::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) + for (std::vector::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) { - iter->mScore = score_map[iter->mMeshParams.getSculptID()]; + iter->setScore(score_map[iter->getId()]); } //sort by "score" std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, - mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); + mPendingRequests.end(), PendingRequestBase::CompareScoreGreater()); } while (!mPendingRequests.empty() && push_count > 0) { - LLMeshRepoThread::LODRequest& request = mPendingRequests.front(); - mThread->loadMeshLOD(request.mMeshParams, request.mLOD); + PendingRequestBase& request = mPendingRequests.front(); + switch (request.getRequestType()) + { + case MESH_REQUEST_LOD: + { + PendingRequestLOD& lod = (PendingRequestLOD&)request; + mThread->loadMeshLOD(lod.mMeshParams, lod.mLOD); + LLMeshRepository::sLODPending--; + break; + } + case MESH_REQUEST_SKIN: + { + PendingRequestUUID& skin = (PendingRequestUUID&)request; + mThread->loadMeshSkinInfo(skin.getId()); + break; + } + + default: + break; + } mPendingRequests.erase(mPendingRequests.begin()); - LLMeshRepository::sLODPending--; push_count--; } } - //send skin info requests - while (!mPendingSkinRequests.empty()) - { - mThread->loadMeshSkinInfo(mPendingSkinRequests.front()); - mPendingSkinRequests.pop(); - } - //send decomposition requests while (!mPendingDecompositionRequests.empty()) { @@ -4231,7 +4262,7 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOV { //first request for this mesh mLoadingSkins[mesh_id].push_back(requesting_obj); - mPendingSkinRequests.push(mesh_id); + mPendingRequests.push_back(PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN)); } } } -- cgit v1.2.3 From 3271408650f080281609c2704c80b6c31c62b3a5 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 11 Nov 2024 22:48:18 +0200 Subject: #1186 Make mesh repository account for avatars when calculating priority --- indra/newview/llmeshrepository.cpp | 81 ++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 26 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 4eb1fdc8de..edb1680eb7 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3740,7 +3740,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para { //first request for this mesh mLoadingMeshes[detail][mesh_id].push_back(vobj); - mPendingRequests.push_back(PendingRequestLOD(mesh_params, detail)); + mPendingRequests.emplace_back(new PendingRequestLOD(mesh_params, detail)); LLMeshRepository::sLODPending++; } } @@ -3799,6 +3799,44 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para return detail; } +F32 calculate_score(LLVOVolume* object) +{ + if (!object) + { + return -1.f; + } + LLDrawable* drawable = object->mDrawable; + if (!drawable) + { + return -1; + } + if (drawable->isState(LLDrawable::RIGGED) || object->isAttachment()) + { + LLVOAvatar* avatar = object->getAvatar(); + LLDrawable* av_drawable = avatar ? avatar->mDrawable : nullptr; + if (avatar && av_drawable) + { + // See LLVOVolume::calcLOD() + F32 radius; + if (avatar->isControlAvatar()) + { + const LLVector3* box = avatar->getLastAnimExtents(); + LLVector3 diag = box[1] - box[0]; + radius = diag.magVec() * 0.5f; + } + else + { + // Volume in a rigged mesh attached to a regular avatar. + const LLVector3* box = avatar->getLastAnimExtents(); + LLVector3 diag = box[1] - box[0]; + radius = diag.magVec(); + } + return radius / llmax(av_drawable->mDistanceWRTCamera, 1.f); + } + } + return drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f); +} + void LLMeshRepository::notifyLoadedMeshes() { //called from main thread LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH); @@ -3998,15 +4036,10 @@ void LLMeshRepository::notifyLoadedMeshes() F32 max_score = 0.f; for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) { - LLVOVolume* object = *obj_iter; - if (object) + F32 cur_score = calculate_score(*obj_iter); + if (cur_score >= 0.f) { - LLDrawable* drawable = object->mDrawable; - if (drawable) - { - F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f); - max_score = llmax(max_score, cur_score); - } + max_score = llmax(max_score, cur_score); } } @@ -4018,15 +4051,10 @@ void LLMeshRepository::notifyLoadedMeshes() F32 max_score = 0.f; for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) { - LLVOVolume* object = *obj_iter; - if (object) + F32 cur_score = calculate_score(*obj_iter); + if (cur_score >= 0.f) { - LLDrawable* drawable = object->mDrawable; - if (drawable) - { - F32 cur_score = drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f); - max_score = llmax(max_score, cur_score); - } + max_score = llmax(max_score, cur_score); } } @@ -4034,9 +4062,9 @@ void LLMeshRepository::notifyLoadedMeshes() } //set "score" for pending requests - for (std::vector::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) + for (std::unique_ptr& req_p : mPendingRequests) { - iter->setScore(score_map[iter->getId()]); + req_p->setScore(score_map[req_p->getId()]); } //sort by "score" @@ -4046,24 +4074,25 @@ void LLMeshRepository::notifyLoadedMeshes() while (!mPendingRequests.empty() && push_count > 0) { - PendingRequestBase& request = mPendingRequests.front(); - switch (request.getRequestType()) + std::unique_ptr& req_p = mPendingRequests.front(); + switch (req_p->getRequestType()) { case MESH_REQUEST_LOD: { - PendingRequestLOD& lod = (PendingRequestLOD&)request; - mThread->loadMeshLOD(lod.mMeshParams, lod.mLOD); + PendingRequestLOD* lod = (PendingRequestLOD*)req_p.get(); + mThread->loadMeshLOD(lod->mMeshParams, lod->mLOD); LLMeshRepository::sLODPending--; break; } case MESH_REQUEST_SKIN: { - PendingRequestUUID& skin = (PendingRequestUUID&)request; - mThread->loadMeshSkinInfo(skin.getId()); + PendingRequestUUID* skin = (PendingRequestUUID*)req_p.get(); + mThread->loadMeshSkinInfo(skin->getId()); break; } default: + LL_ERRS() << "Unknown request type in LLMeshRepository::notifyLoadedMeshes" << LL_ENDL; break; } mPendingRequests.erase(mPendingRequests.begin()); @@ -4262,7 +4291,7 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOV { //first request for this mesh mLoadingSkins[mesh_id].push_back(requesting_obj); - mPendingRequests.push_back(PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN)); + mPendingRequests.emplace_back(new PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN)); } } } -- cgit v1.2.3 From 8e17f0c094e12faea9b9268330b24c77913fe9f3 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 18 Nov 2024 20:35:33 +0200 Subject: #1186 Fix skin request not being counted for the total --- indra/newview/llmeshrepository.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index edb1680eb7..2864619224 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -548,8 +548,9 @@ LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMate return ppTex ? (*ppTex).get() : NULL; } -volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0; -volatile S32 LLMeshRepoThread::sActiveLODRequests = 0; +std::atomic LLMeshRepoThread::sActiveHeaderRequests = 0; +std::atomic LLMeshRepoThread::sActiveLODRequests = 0; +std::atomic LLMeshRepoThread::sActiveSkinRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN; S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN; @@ -674,7 +675,9 @@ public: LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 requested_bytes) : LLMeshHandlerBase(offset, requested_bytes), mMeshID(id) - {} + { + LLMeshRepoThread::incActiveSkinRequests(); + } virtual ~LLMeshSkinInfoHandler(); protected: @@ -1701,6 +1704,20 @@ void LLMeshRepoThread::decActiveHeaderRequests() --LLMeshRepoThread::sActiveHeaderRequests; } +//static +void LLMeshRepoThread::incActiveSkinRequests() +{ + LLMutexLock lock(gMeshRepo.mThread->mMutex); + ++LLMeshRepoThread::sActiveSkinRequests; +} + +//static +void LLMeshRepoThread::decActiveSkinRequests() +{ + LLMutexLock lock(gMeshRepo.mThread->mMutex); + --LLMeshRepoThread::sActiveSkinRequests; +} + //return false if failed to get header bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry) { @@ -3448,6 +3465,7 @@ LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler() { LL_WARNS(LOG_MESH) << "deleting unprocessed request handler (may be ok on exit)" << LL_ENDL; } + LLMeshRepoThread::decActiveSkinRequests(); } void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) @@ -4014,7 +4032,7 @@ void LLMeshRepository::notifyLoadedMeshes() mUploadErrorQ.pop(); } - S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests; + S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests + LLMeshRepoThread::sActiveSkinRequests; if (active_count < LLMeshRepoThread::sRequestLowWater) { S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count; -- cgit v1.2.3 From e20f4c0e4f10151bb5e5f208ea49b6065dd667ca Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 19 Nov 2024 19:47:23 +0200 Subject: #1186 Fix mesh queues getting overfilled Account for queue size instead of just active request Reduce mutex locking Prioritize skininfo queue over lod queue to lessen issues with t-poses --- indra/newview/llmeshrepository.cpp | 139 ++++++++++++++++++++++++------------- 1 file changed, 92 insertions(+), 47 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 2864619224..f58ceae852 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -933,6 +933,50 @@ void LLMeshRepoThread::run() // in relatively similar manners, remake code to simplify/unify the process, // like processRequests(&requestQ, fetchFunction); which does same thing for each element + if (mHttpRequestSet.size() < sRequestHighWater + && !mSkinRequests.empty()) + { + if (!mSkinRequests.empty()) + { + std::list incomplete; + while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) + { + + mMutex->lock(); + auto req = mSkinRequests.front(); + mSkinRequests.pop_front(); + mMutex->unlock(); + if (req.isDelayed()) + { + incomplete.emplace_back(req); + } + else if (!fetchMeshSkinInfo(req.mId, req.canRetry())) + { + if (req.canRetry()) + { + req.updateTime(); + incomplete.emplace_back(req); + } + else + { + LLMutexLock locker(mMutex); + mSkinUnavailableQ.push_back(req); + LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL; + } + } + } + + if (!incomplete.empty()) + { + LLMutexLock locker(mMutex); + for (const auto& req : incomplete) + { + mSkinRequests.push_back(req); + } + } + } + } + if (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) { std::list incomplete; @@ -982,50 +1026,6 @@ void LLMeshRepoThread::run() } } - if (mHttpRequestSet.size() < sRequestHighWater - && !mSkinRequests.empty()) - { - if (!mSkinRequests.empty()) - { - std::list incomplete; - while (!mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) - { - - mMutex->lock(); - auto req = mSkinRequests.front(); - mSkinRequests.pop_front(); - mMutex->unlock(); - if (req.isDelayed()) - { - incomplete.emplace_back(req); - } - else if (!fetchMeshSkinInfo(req.mId, req.canRetry())) - { - if (req.canRetry()) - { - req.updateTime(); - incomplete.emplace_back(req); - } - else - { - LLMutexLock locker(mMutex); - mSkinUnavailableQ.push_back(req); - LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL; - } - } - } - - if (!incomplete.empty()) - { - LLMutexLock locker(mMutex); - for (const auto& req : incomplete) - { - mSkinRequests.push_back(req); - } - } - } - } - if (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) { std::list incomplete; @@ -1234,6 +1234,42 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } } +void LLMeshRepoThread::loadMeshLODs(const lod_list_t& list) +{ //could be called from any thread + LLMutexLock lock(mMutex); + LLMutexLock header_lock(mHeaderMutex); + for (auto lod_pair : list) + { + const LLVolumeParams& mesh_params = lod_pair.first; + const LLUUID& mesh_id = mesh_params.getSculptID(); + S32 lod = lod_pair.second; + mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); + if (iter != mMeshHeader.end()) + { // if we have the header, request LOD byte range + LODRequest req(mesh_params, lod); + { + mLODReqQ.push(req); + LLMeshRepository::sLODProcessing++; + } + } + else + { + HeaderRequest req(mesh_params); + pending_lod_map::iterator pending = mPendingLOD.find(mesh_id); + if (pending != mPendingLOD.end()) + { // append this lod request to existing header request + pending->second.push_back(lod); + llassert(pending->second.size() <= LLModel::NUM_LODS); + } + else + { // if no header request is pending, fetch header + mHeaderReqQ.push(req); + mPendingLOD[mesh_id].push_back(lod); + } + } + } +} + // Mutex: must be holding mMutex when called void LLMeshRepoThread::setGetMeshCap(const std::string & mesh_cap) { @@ -4032,8 +4068,12 @@ void LLMeshRepository::notifyLoadedMeshes() mUploadErrorQ.pop(); } + // mPendingRequests go into queues, queues go into active http requests. + // Checking sRequestHighWater to keep queues at least somewhat populated + // for faster transition into http S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests + LLMeshRepoThread::sActiveSkinRequests; - if (active_count < LLMeshRepoThread::sRequestLowWater) + active_count += (S32)(mThread->mLODReqQ.size() + mThread->mHeaderReqQ.size() + mThread->mSkinInfoQ.size()); + if (active_count < LLMeshRepoThread::sRequestHighWater) { S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count; @@ -4089,7 +4129,8 @@ void LLMeshRepository::notifyLoadedMeshes() std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, mPendingRequests.end(), PendingRequestBase::CompareScoreGreater()); } - + LLMeshRepoThread::lod_list_t pending_lods; // to avoid locking on each operation, make a list beforehand + pending_lods.reserve(push_count); while (!mPendingRequests.empty() && push_count > 0) { std::unique_ptr& req_p = mPendingRequests.front(); @@ -4098,7 +4139,7 @@ void LLMeshRepository::notifyLoadedMeshes() case MESH_REQUEST_LOD: { PendingRequestLOD* lod = (PendingRequestLOD*)req_p.get(); - mThread->loadMeshLOD(lod->mMeshParams, lod->mLOD); + pending_lods.emplace_back(lod->mMeshParams, lod->mLOD); LLMeshRepository::sLODPending--; break; } @@ -4116,6 +4157,10 @@ void LLMeshRepository::notifyLoadedMeshes() mPendingRequests.erase(mPendingRequests.begin()); push_count--; } + if (!pending_lods.empty()) + { + mThread->loadMeshLODs(pending_lods); + } } //send decomposition requests -- cgit v1.2.3 From a428b7dfbd7d5b766b2ae6e4fbd2c8c510ee19a1 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 5 Dec 2024 01:32:54 +0200 Subject: #1186 Improve handling of duplciate requests --- indra/newview/llmeshrepository.cpp | 100 ++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 45 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index f58ceae852..e098ce80bc 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1109,7 +1109,7 @@ void LLMeshRepoThread::run() } else { - LL_DEBUGS() << "mDecompositionRequests failed: " << req.mId << LL_ENDL; + LL_DEBUGS(LOG_MESH) << "mDecompositionRequests failed: " << req.mId << LL_ENDL; } } } @@ -1145,7 +1145,7 @@ void LLMeshRepoThread::run() } else { - LL_DEBUGS() << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL; + LL_DEBUGS(LOG_MESH) << "mPhysicsShapeRequests failed: " << req.mId << LL_ENDL; } } } @@ -1206,6 +1206,25 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) const LLUUID& mesh_id = mesh_params.getSculptID(); LLMutexLock lock(mMutex); LLMutexLock header_lock(mHeaderMutex); + loadMeshLOD(mesh_id, mesh_params, lod); +} + +void LLMeshRepoThread::loadMeshLODs(const lod_list_t& list) +{ //could be called from any thread + LLMutexLock lock(mMutex); + LLMutexLock header_lock(mHeaderMutex); + for (auto lod_pair : list) + { + const LLVolumeParams& mesh_params = lod_pair.first; + const LLUUID& mesh_id = mesh_params.getSculptID(); + S32 lod = lod_pair.second; + loadMeshLOD(mesh_id, mesh_params, lod); + } +} + +void LLMeshRepoThread::loadMeshLOD(const LLUUID& mesh_id, const LLVolumeParams& mesh_params, S32 lod) +{ + // must be mutex locked by caller mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); if (iter != mMeshHeader.end()) { //if we have the header, request LOD byte range @@ -1222,50 +1241,25 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) pending_lod_map::iterator pending = mPendingLOD.find(mesh_id); if (pending != mPendingLOD.end()) - { //append this lod request to existing header request - pending->second.push_back(lod); - llassert(pending->second.size() <= LLModel::NUM_LODS); - } - else - { //if no header request is pending, fetch header - mHeaderReqQ.push(req); - mPendingLOD[mesh_id].push_back(lod); - } - } -} - -void LLMeshRepoThread::loadMeshLODs(const lod_list_t& list) -{ //could be called from any thread - LLMutexLock lock(mMutex); - LLMutexLock header_lock(mHeaderMutex); - for (auto lod_pair : list) - { - const LLVolumeParams& mesh_params = lod_pair.first; - const LLUUID& mesh_id = mesh_params.getSculptID(); - S32 lod = lod_pair.second; - mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); - if (iter != mMeshHeader.end()) - { // if we have the header, request LOD byte range - LODRequest req(mesh_params, lod); + { + //append this lod request to existing header request + if (lod < LLModel::NUM_LODS && lod >= 0) { - mLODReqQ.push(req); - LLMeshRepository::sLODProcessing++; + pending->second[lod]++; } + else + { + LL_WARNS(LOG_MESH) << "Invalid LOD request: " << lod << "for mesh" << mesh_id << LL_ENDL; + } + llassert_msg(lod < LLModel::NUM_LODS, "Requested lod is out of bounds"); } else { - HeaderRequest req(mesh_params); - pending_lod_map::iterator pending = mPendingLOD.find(mesh_id); - if (pending != mPendingLOD.end()) - { // append this lod request to existing header request - pending->second.push_back(lod); - llassert(pending->second.size() <= LLModel::NUM_LODS); - } - else - { // if no header request is pending, fetch header - mHeaderReqQ.push(req); - mPendingLOD[mesh_id].push_back(lod); - } + //if no header request is pending, fetch header + mHeaderReqQ.push(req); + auto& array = mPendingLOD[mesh_id]; + std::fill(array.begin(), array.end(), 0); + array[lod]++; } } } @@ -2024,11 +2018,27 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes pending_lod_map::iterator iter = mPendingLOD.find(mesh_id); if (iter != mPendingLOD.end()) { - for (U32 i = 0; i < iter->second.size(); ++i) + for (S32 i = 0; i < iter->second.size(); ++i) { - LODRequest req(mesh_params, iter->second[i]); - mLODReqQ.push(req); - LLMeshRepository::sLODProcessing++; + if (iter->second[i] > 1) + { + // mLoadingMeshes should be protecting from dupplciates, but looks + // like this is possible if object rezzes, unregisterMesh, then + // rezzes again before first request completes. + // mLoadingMeshes might need to change a bit to not rerequest if + // mesh is already pending. + // + // Todo: Improve mLoadingMeshes and once done turn this into an assert. + // Low priority since such situation should be relatively rare + LL_INFOS(LOG_MESH) << "Multiple dupplicate requests for mesd ID: " << mesh_id << " LOD: " << i + << LL_ENDL; + } + if (iter->second[i] > 0) + { + LODRequest req(mesh_params, i); + mLODReqQ.push(req); + LLMeshRepository::sLODProcessing++; + } } mPendingLOD.erase(iter); } -- cgit v1.2.3 From 1a3b221ea4628f2161332c2b6d0163a49bb8ab20 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 28 Jan 2025 17:10:47 +0200 Subject: #1186 Speed up mesh header post processing 1. Don't do an extra skin request unless there is info about a skin 2. Proccess lods on the go if they were in initial packet --- indra/newview/llmeshrepository.cpp | 71 +++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 16 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index e098ce80bc..b5784580f5 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1950,6 +1950,10 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes LLMeshHeader header; llssize header_size = 0; + S32 skin_offset = -1; + S32 skin_size = -1; + S32 lod_offset[LLModel::NUM_LODS] = { -1 }; + S32 lod_size[LLModel::NUM_LODS] = { -1 }; if (data_size > 0) { llssize dsize = data_size; @@ -1983,6 +1987,10 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes else if (LLMeshRepository::getActualMeshLOD(header, 0) >= 0) { header_size += stream.tellg(); + skin_offset = header.mSkinOffset; + skin_size = header.mSkinSize; + memcpy(lod_offset, header.mLodOffset, sizeof(lod_offset)); + memcpy(lod_size, header.mLodSize, sizeof(lod_size)); } } else @@ -2001,26 +2009,48 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes } // immediately request SkinInfo since we'll need it before we can render any LoD if it is present + if (skin_offset >= 0 && skin_size > 0) { - LLMutexLock lock(gMeshRepo.mMeshMutex); + { + LLMutexLock lock(gMeshRepo.mMeshMutex); + + if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end()) + { + gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info + } + } - if (gMeshRepo.mLoadingSkins.find(mesh_id) == gMeshRepo.mLoadingSkins.end()) + S32 offset = (S32)header_size + skin_offset; + bool request_skin = true; + if (offset + skin_size < MESH_HEADER_SIZE) { - gMeshRepo.mLoadingSkins[mesh_id] = {}; // add an empty vector to indicate to main thread that we are loading skin info + request_skin = !skinInfoReceived(mesh_id, data + offset, skin_size); + } + if (request_skin) + { + mSkinRequests.push_back(UUIDBasedRequest(mesh_id)); } } - fetchMeshSkinInfo(mesh_id); - - LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. + std::array pending_lods; + bool has_pending_lods = false; + { + LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. + pending_lod_map::iterator iter = mPendingLOD.find(mesh_id); + if (iter != mPendingLOD.end()) + { + pending_lods = iter->second; + mPendingLOD.erase(iter); + has_pending_lods = true; + } + } //check for pending requests - pending_lod_map::iterator iter = mPendingLOD.find(mesh_id); - if (iter != mPendingLOD.end()) + if (has_pending_lods) { - for (S32 i = 0; i < iter->second.size(); ++i) + for (S32 i = 0; i < pending_lods.size(); ++i) { - if (iter->second[i] > 1) + if (pending_lods[i] > 1) { // mLoadingMeshes should be protecting from dupplciates, but looks // like this is possible if object rezzes, unregisterMesh, then @@ -2033,14 +2063,24 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes LL_INFOS(LOG_MESH) << "Multiple dupplicate requests for mesd ID: " << mesh_id << " LOD: " << i << LL_ENDL; } - if (iter->second[i] > 0) + if (pending_lods[i] > 0 && lod_size[i] > 0) { - LODRequest req(mesh_params, i); - mLODReqQ.push(req); - LLMeshRepository::sLODProcessing++; + // try to load from data we just received + bool request_lod = true; + S32 offset = (S32)header_size + lod_offset[i]; + if (offset + lod_size[i] <= MESH_HEADER_SIZE) + { + // initial request is 4096 bytes, it's big enough to fit this lod + request_lod = lodReceived(mesh_params, i, data + offset, lod_size[i]) != MESH_OK; + } + if (request_lod) + { + LODRequest req(mesh_params, i); + mLODReqQ.push(req); + LLMeshRepository::sLODProcessing++; + } } } - mPendingLOD.erase(iter); } } @@ -2267,7 +2307,6 @@ LLMeshUploadThread::~LLMeshUploadThread() mHttpRequest = NULL; delete mMutex; mMutex = NULL; - } LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread) -- cgit v1.2.3 From 8456572db0d576d3a90d2c5d8a5a41ff298af435 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 28 Jan 2025 20:40:41 +0200 Subject: #1186 Profiling --- indra/newview/llmeshrepository.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index b5784580f5..8381d3e2e2 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -911,6 +911,7 @@ void LLMeshRepoThread::run() // On the other hand, this may actually be an effective and efficient scheme... mSignal->wait(); + LL_PROFILE_ZONE_NAMED("mesh_thread_loop") if (LLApp::isExiting()) { @@ -1944,6 +1945,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; const LLUUID mesh_id = mesh_params.getSculptID(); LLSD header_data; @@ -2089,6 +2091,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; if (data == NULL || data_size == 0) { return MESH_NO_DATA; @@ -2131,6 +2134,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; LLSD skin; if (data_size > 0) @@ -2180,6 +2184,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; LLSD decomp; if (data_size > 0) @@ -2216,6 +2221,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3 EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; LLSD physics_shape; LLModel::Decomposition* d = new LLModel::Decomposition(); @@ -3047,6 +3053,8 @@ void LLMeshRepoThread::notifyLoadedMeshes() return; } + LL_PROFILE_ZONE_SCOPED; + if (!mLoadedQ.empty()) { std::deque loaded_queue; @@ -3367,6 +3375,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, U8 * data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; LLUUID mesh_id = mMeshParams.getSculptID(); bool success = (!MESH_HEADER_PROCESS_FAILED) && ((data != NULL) == (data_size > 0)); // if we have data but no size or have size but no data, something is wrong; -- cgit v1.2.3 From 52fb53a4d6e0856c96044c654970dcbb1cc1f235 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 29 Jan 2025 23:05:35 +0200 Subject: #3488 Make mesh_header_map a bit simpler preparation for further work --- indra/newview/llmeshrepository.cpp | 57 ++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 27 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 8381d3e2e2..204834f8de 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1373,7 +1373,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) mHeaderMutex->lock(); - auto header_it = mMeshHeader.find(mesh_id); + mesh_header_map::const_iterator header_it = mMeshHeader.find(mesh_id); if (header_it == mMeshHeader.end()) { //we have no header info for this mesh, do nothing mHeaderMutex->unlock(); @@ -1382,11 +1382,11 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) ++LLMeshRepository::sMeshRequestCount; bool ret = true; - U32 header_size = header_it->second.first; + const LLMeshHeader& header = header_it->second; + U32 header_size = header.mHeaderSize; if (header_size > 0) { - const LLMeshHeader& header = header_it->second.second; S32 version = header.mVersion; S32 offset = header_size + header.mSkinOffset; @@ -1506,12 +1506,12 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) } ++LLMeshRepository::sMeshRequestCount; - U32 header_size = header_it->second.first; + const auto& header = header_it->second; + U32 header_size = header.mHeaderSize; bool ret = true; if (header_size > 0) { - const auto& header = header_it->second.second; S32 version = header.mVersion; S32 offset = header_size + header.mPhysicsConvexOffset; S32 size = header.mPhysicsConvexSize; @@ -1614,12 +1614,12 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) } ++LLMeshRepository::sMeshRequestCount; - U32 header_size = header_it->second.first; + const auto& header = header_it->second; + U32 header_size = header.mHeaderSize; bool ret = true; if (header_size > 0) { - const auto& header = header_it->second.second; S32 version = header.mVersion; S32 offset = header_size + header.mPhysicsMeshOffset; S32 size = header.mPhysicsMeshSize; @@ -1764,7 +1764,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c if (size > 0) { // *NOTE: if the header size is ever more than 4KB, this will break - U8 buffer[MESH_HEADER_SIZE]; + static thread_local U8 buffer[MESH_HEADER_SIZE]; S32 bytes = llmin(size, MESH_HEADER_SIZE); LLMeshRepository::sCacheBytesRead += bytes; ++LLMeshRepository::sCacheReads; @@ -1834,10 +1834,10 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, ++LLMeshRepository::sMeshRequestCount; bool retval = true; - U32 header_size = header_it->second.first; + const auto& header = header_it->second; + U32 header_size = header.mHeaderSize; if (header_size > 0) { - const auto& header = header_it->second.second; S32 version = header.mVersion; S32 offset = header_size + header.mLodOffset[lod]; S32 size = header.mLodSize[lod]; @@ -1988,7 +1988,8 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes // make sure there is at least one lod, function returns -1 and marks as 404 otherwise else if (LLMeshRepository::getActualMeshLOD(header, 0) >= 0) { - header_size += stream.tellg(); + header.mHeaderSize = stream.tellg(); + header_size += header.mHeaderSize; skin_offset = header.mSkinOffset; skin_size = header.mSkinSize; memcpy(lod_offset, header.mLodOffset, sizeof(lod_offset)); @@ -2006,7 +2007,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes { LLMutexLock lock(mHeaderMutex); - mMeshHeader[mesh_id] = { (U32)header_size, header }; + mMeshHeader[mesh_id] = header; LLMeshRepository::sCacheBytesHeaders += (U32)header_size; } @@ -3163,9 +3164,11 @@ S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo if (iter != mMeshHeader.end()) { - auto& header = iter->second.second; - - return LLMeshRepository::getActualMeshLOD(header, lod); + auto& header = iter->second; + if (header.mHeaderSize > 0) + { + return LLMeshRepository::getActualMeshLOD(header, lod); + } } return lod; @@ -3411,8 +3414,8 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b LLMeshRepoThread::mesh_header_map::iterator iter = gMeshRepo.mThread->mMeshHeader.find(mesh_id); if (iter != gMeshRepo.mThread->mMeshHeader.end()) { - header_bytes = (S32)iter->second.first; - header = iter->second.second; + header = iter->second; + header_bytes = header.mHeaderSize; } if (header_bytes > 0 @@ -4553,9 +4556,9 @@ bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) { LLMutexLock lock(mHeaderMutex); mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); - if (iter != mMeshHeader.end() && iter->second.first > 0) + if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0) { - LLMeshHeader &mesh = iter->second.second; + LLMeshHeader &mesh = iter->second; if (mesh.mPhysicsMeshSize > 0) { return true; @@ -4569,9 +4572,9 @@ bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id) { LLMutexLock lock(mHeaderMutex); mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); - if (iter != mMeshHeader.end() && iter->second.first > 0) + if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0) { - LLMeshHeader& mesh = iter->second.second; + LLMeshHeader& mesh = iter->second; if (mesh.mSkinOffset >= 0 && mesh.mSkinSize > 0) { @@ -4607,9 +4610,9 @@ S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) { LLMutexLock lock(mThread->mHeaderMutex); LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); - if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) + if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0) { - const LLMeshHeader& header = iter->second.second; + const LLMeshHeader& header = iter->second; if (header.m404) { @@ -4713,9 +4716,9 @@ F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* by { LLMutexLock lock(mThread->mHeaderMutex); LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); - if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) + if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0) { - result = getStreamingCostLegacy(iter->second.second, radius, bytes, bytes_visible, lod, unscaled_value); + result = getStreamingCostLegacy(iter->second, radius, bytes, bytes_visible, lod, unscaled_value); } } if (result > 0.f) @@ -5027,9 +5030,9 @@ bool LLMeshRepository::getCostData(LLUUID mesh_id, LLMeshCostData& data) { LLMutexLock lock(mThread->mHeaderMutex); LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); - if (iter != mThread->mMeshHeader.end() && iter->second.first > 0) + if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0) { - LLMeshHeader& header = iter->second.second; + LLMeshHeader& header = iter->second; bool header_invalid = (header.m404 || header.mLodSize[0] <= 0 -- cgit v1.2.3 From c405cd379a736722740ac241d36bcffb88c236c2 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 30 Jan 2025 00:50:49 +0200 Subject: #3488 Avoid reading meshes from disk when not nessesary LLMeshHeaderHandler preallocates file with empty data, there is no point reading large empty sections so write presense flags at the start of the file --- indra/newview/llmeshrepository.cpp | 219 ++++++++++++++++++++++++++++++++----- 1 file changed, 193 insertions(+), 26 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 204834f8de..2969b42ca5 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -343,6 +343,8 @@ static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch"); LLMeshRepository gMeshRepo; +const U32 CACHE_PREAMBLE_VERSION = 1; +const S32 CACHE_PREAMBLE_SIZE = sizeof(U32) * 3; //version, header_size, flags const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space @@ -826,6 +828,14 @@ void log_upload_error(LLCore::HttpStatus status, const LLSD& content, gMeshRepo.uploadError(args); } +void write_preamble(LLFileSystem &file, S32 header_bytes, S32 flags) +{ + LLMeshRepository::sCacheBytesWritten += CACHE_PREAMBLE_SIZE; + file.write((U8*)&CACHE_PREAMBLE_VERSION, sizeof(U32)); + file.write((U8*)&header_bytes, sizeof(U32)); + file.write((U8*)&flags, sizeof(U32)); +} + LLMeshRepoThread::LLMeshRepoThread() : LLThread("mesh repo"), mHttpRequest(NULL), @@ -1387,18 +1397,19 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) if (header_size > 0) { - S32 version = header.mVersion; S32 offset = header_size + header.mSkinOffset; S32 size = header.mSkinSize; + bool in_cache = header.mSkinInCache; mHeaderMutex->unlock(); if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check cache for mesh skin info + S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE; LLFileSystem file(mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset + size) + if (in_cache && file.getSize() >= disk_ofset + size) { U8* buffer = new(std::nothrow) U8[size]; if (!buffer) @@ -1418,7 +1429,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) } LLMeshRepository::sCacheBytesRead += size; ++LLMeshRepository::sCacheReads; - file.seek(offset); + file.seek(disk_ofset); file.read(buffer, size); //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1515,14 +1526,16 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) S32 version = header.mVersion; S32 offset = header_size + header.mPhysicsConvexOffset; S32 size = header.mPhysicsConvexSize; + bool in_cache = header.mPhysicsConvexInCache; mHeaderMutex->unlock(); if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { - //check cache for mesh skin info + // check cache for mesh decomposition + S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE; LLFileSystem file(mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset+size) + if (in_cache && file.getSize() >= disk_ofset + size) { U8* buffer = new(std::nothrow) U8[size]; if (!buffer) @@ -1541,7 +1554,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) LLMeshRepository::sCacheBytesRead += size; ++LLMeshRepository::sCacheReads; - file.seek(offset); + file.seek(disk_ofset); file.read(buffer, size); //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1623,18 +1636,21 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) S32 version = header.mVersion; S32 offset = header_size + header.mPhysicsMeshOffset; S32 size = header.mPhysicsMeshSize; + bool in_cache = header.mPhysicsMeshInCache; mHeaderMutex->unlock(); + // todo: check header.mHasPhysicsMesh if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check cache for mesh physics shape info + S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE; LLFileSystem file(mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset+size) + if (in_cache && file.getSize() >= disk_ofset +size) { LLMeshRepository::sCacheBytesRead += size; ++LLMeshRepository::sCacheReads; - file.seek(offset); + file.seek(disk_ofset); U8* buffer = new(std::nothrow) U8[size]; if (!buffer) { @@ -1764,17 +1780,35 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c if (size > 0) { // *NOTE: if the header size is ever more than 4KB, this will break - static thread_local U8 buffer[MESH_HEADER_SIZE]; - S32 bytes = llmin(size, MESH_HEADER_SIZE); + const S32 DISK_MINIMAL_READ = 4096; + static thread_local U8 buffer[DISK_MINIMAL_READ * 2]; + S32 bytes = llmin(size, DISK_MINIMAL_READ); LLMeshRepository::sCacheBytesRead += bytes; ++LLMeshRepository::sCacheReads; + file.read(buffer, bytes); - if (headerReceived(mesh_params, buffer, bytes) == MESH_OK) + + U32 version = 0; + memcpy(&version, buffer, sizeof(U32)); + if (version == CACHE_PREAMBLE_VERSION) { - LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL; + S32 header_size = 0; + memcpy(&header_size, buffer + sizeof(U32), sizeof(S32)); + if (header_size + CACHE_PREAMBLE_SIZE > DISK_MINIMAL_READ) + { + bytes = llmin(size , DISK_MINIMAL_READ * 2); + file.read(buffer + DISK_MINIMAL_READ, bytes - DISK_MINIMAL_READ); + } + U32 flags = 0; + memcpy(&flags, buffer + 2 * sizeof(U32), sizeof(U32)); + // Todo: parse and pass flags, they are the reason for the preamble + if (headerReceived(mesh_params, buffer + CACHE_PREAMBLE_SIZE, bytes, flags) == MESH_OK) + { + LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL; - // Found mesh in cache - return true; + // Found mesh in cache + return true; + } } } } @@ -1841,14 +1875,15 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, S32 version = header.mVersion; S32 offset = header_size + header.mLodOffset[lod]; S32 size = header.mLodSize[lod]; + bool in_cache = header.mLodInCache[lod]; mHeaderMutex->unlock(); if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { - + S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE; //check cache for mesh asset LLFileSystem file(mesh_id, LLAssetType::AT_MESH); - if (file.getSize() >= offset+size) + if (in_cache && file.getSize() >= disk_ofset + size) { U8* buffer = new(std::nothrow) U8[size]; if (!buffer) @@ -1868,7 +1903,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, } LLMeshRepository::sCacheBytesRead += size; ++LLMeshRepository::sCacheReads; - file.seek(offset); + file.seek(disk_ofset); file.read(buffer, size); //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1943,7 +1978,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, return retval; } -EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) +EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size, U32 flags) { LL_PROFILE_ZONE_SCOPED; const LLUUID mesh_id = mesh_params.getSculptID(); @@ -1988,12 +2023,40 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes // make sure there is at least one lod, function returns -1 and marks as 404 otherwise else if (LLMeshRepository::getActualMeshLOD(header, 0) >= 0) { - header.mHeaderSize = stream.tellg(); + header.mHeaderSize = (S32)stream.tellg(); header_size += header.mHeaderSize; skin_offset = header.mSkinOffset; skin_size = header.mSkinSize; + memcpy(lod_offset, header.mLodOffset, sizeof(lod_offset)); memcpy(lod_size, header.mLodSize, sizeof(lod_size)); + + if (flags != 0) + { + header.setFromFlags(flags); + } + else + { + if (header.mSkinSize > 0 && header_size + header.mSkinOffset + header.mSkinSize < data_size) + { + header.mSkinInCache = true; + } + if (header.mPhysicsConvexSize > 0 && header_size + header.mPhysicsConvexOffset + header.mPhysicsConvexSize < data_size) + { + header.mPhysicsConvexInCache = true; + } + if (header.mPhysicsMeshSize > 0 && header_size + header.mPhysicsMeshOffset + header.mPhysicsMeshSize < data_size) + { + header.mPhysicsMeshInCache = true; + } + for (S32 i = 0; i < LLModel::NUM_LODS; ++i) + { + if (lod_size[i] > 0 && header_size + lod_offset[i] + lod_size[i] < data_size) + { + header.mLodInCache[i] = true; + } + } + } } } else @@ -2025,7 +2088,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes S32 offset = (S32)header_size + skin_offset; bool request_skin = true; - if (offset + skin_size < MESH_HEADER_SIZE) + if (offset + skin_size < data_size) { request_skin = !skinInfoReceived(mesh_id, data + offset, skin_size); } @@ -2071,7 +2134,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes // try to load from data we just received bool request_lod = true; S32 offset = (S32)header_size + lod_offset[i]; - if (offset + lod_size[i] <= MESH_HEADER_SIZE) + if (offset + lod_size[i] <= data_size) { // initial request is 4096 bytes, it's big enough to fit this lod request_lod = lodReceived(mesh_params, i, data + offset, lod_size[i]) != MESH_OK; @@ -3440,7 +3503,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b // LLSD is smart and can work like smart pointer, is not thread safe. gMeshRepo.mThread->mHeaderMutex->unlock(); - S32 bytes = lod_bytes + header_bytes; + S32 bytes = lod_bytes + header_bytes + CACHE_PREAMBLE_SIZE; // It's possible for the remote asset to have more data than is needed for the local cache @@ -3453,6 +3516,11 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b LLMeshRepository::sCacheBytesWritten += data_size; ++LLMeshRepository::sCacheWrites; + // write preamble + U32 flags = header.getFlags(); + write_preamble(file, header_bytes, flags); + + // write header file.write(data, data_size); S32 remaining = bytes - file.tell(); @@ -3521,11 +3589,35 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body // good fetch from sim, write to cache LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - S32 offset = mOffset; + S32 offset = mOffset + CACHE_PREAMBLE_SIZE; S32 size = mRequestedBytes; if (file.getSize() >= offset+size) { + S32 header_bytes = 0; + U32 flags = 0; + { + LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + + LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); + if (header_it != gMeshRepo.mThread->mMeshHeader.end()) + { + LLMeshHeader& header = header_it->second; + // update header + if (!header.mLodInCache[mLOD]) + { + header.mLodInCache[mLOD] = true; + header_bytes = header.mHeaderSize; + flags = header.getFlags(); + } + // todo: handle else because we shouldn't have requested twice? + } + } + if (flags > 0) + { + write_preamble(file, header_bytes, flags); + } + file.seek(offset); file.write(data, size); LLMeshRepository::sCacheBytesWritten += size; @@ -3586,13 +3678,38 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* // good fetch from sim, write to cache LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - S32 offset = mOffset; + S32 offset = mOffset + CACHE_PREAMBLE_SIZE; S32 size = mRequestedBytes; if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesWritten += size; ++LLMeshRepository::sCacheWrites; + + S32 header_bytes = 0; + U32 flags = 0; + { + LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + + LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); + if (header_it != gMeshRepo.mThread->mMeshHeader.end()) + { + LLMeshHeader& header = header_it->second; + // update header + if (!header.mSkinInCache) + { + header.mSkinInCache = true; + header_bytes = header.mHeaderSize; + flags = header.getFlags(); + } + // todo: handle else because we shouldn't have requested twice? + } + } + if (flags > 0) + { + write_preamble(file, header_bytes, flags); + } + file.seek(offset); file.write(data, size); } @@ -3636,13 +3753,38 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S // good fetch from sim, write to cache LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - S32 offset = mOffset; + S32 offset = mOffset + CACHE_PREAMBLE_SIZE; S32 size = mRequestedBytes; if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesWritten += size; ++LLMeshRepository::sCacheWrites; + + S32 header_bytes = 0; + U32 flags = 0; + { + LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + + LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); + if (header_it != gMeshRepo.mThread->mMeshHeader.end()) + { + LLMeshHeader& header = header_it->second; + // update header + if (!header.mPhysicsConvexInCache) + { + header.mPhysicsConvexInCache = true; + header_bytes = header.mHeaderSize; + flags = header.getFlags(); + } + // todo: handle else because we shouldn't have requested twice? + } + } + if (flags > 0) + { + write_preamble(file, header_bytes, flags); + } + file.seek(offset); file.write(data, size); } @@ -3684,13 +3826,38 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 // good fetch from sim, write to cache for caching LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - S32 offset = mOffset; + S32 offset = mOffset + CACHE_PREAMBLE_SIZE; S32 size = mRequestedBytes; if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesWritten += size; ++LLMeshRepository::sCacheWrites; + + S32 header_bytes = 0; + U32 flags = 0; + { + LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + + LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); + if (header_it != gMeshRepo.mThread->mMeshHeader.end()) + { + LLMeshHeader& header = header_it->second; + // update header + if (!header.mPhysicsMeshInCache) + { + header.mPhysicsMeshInCache = true; + header_bytes = header.mHeaderSize; + flags = header.getFlags(); + } + // todo: handle else because we shouldn't have requested twice? + } + } + if (flags > 0) + { + write_preamble(file, header_bytes, flags); + } + file.seek(offset); file.write(data, size); } -- cgit v1.2.3 From e97976831d883d9c8ff40a0442a5905ac05dd45a Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 30 Jan 2025 23:02:53 +0200 Subject: #3488 Minimize allocations --- indra/newview/llmeshrepository.cpp | 98 +++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 55 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 2969b42ca5..821288429d 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -895,6 +895,8 @@ LLMeshRepoThread::~LLMeshRepoThread() mHeaderMutex = NULL; delete mSignal; mSignal = NULL; + delete[] mDiskCacheBuffer; + mDiskCacheBuffer = NULL; } void LLMeshRepoThread::run() @@ -1275,6 +1277,41 @@ void LLMeshRepoThread::loadMeshLOD(const LLUUID& mesh_id, const LLVolumeParams& } } +U8* LLMeshRepoThread::getDiskCacheBuffer(S32 size) +{ + if (mDiskCacheBufferSize < size) + { + const S32 MINIMUM_BUFFER_SIZE = 8192; // a minimum to avoid frequent early reallocations + size = llmax(MINIMUM_BUFFER_SIZE, size); + delete[] mDiskCacheBuffer; + try + { + mDiskCacheBuffer = new U8[size]; + } + catch (std::bad_alloc&) + { + LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh thread's buffer, size: " << size << LL_ENDL; + mDiskCacheBuffer = NULL; + + // Not sure what size is reasonable + // but if 30MB allocation failed, we definetely have issues + const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB + if (size < MAX_SIZE) + { + LLAppViewer::instance()->outOfMemorySoftQuit(); + } // else ignore failures for anomalously large data + } + mDiskCacheBufferSize = size; + } + else + { + // reusing old buffer, reset heading bytes to ensure + // old content won't be parsable if something fails. + memset(mDiskCacheBuffer, 0, 16); + } + return mDiskCacheBuffer; +} + // Mutex: must be holding mMutex when called void LLMeshRepoThread::setGetMeshCap(const std::string & mesh_cap) { @@ -1411,19 +1448,9 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (in_cache && file.getSize() >= disk_ofset + size) { - U8* buffer = new(std::nothrow) U8[size]; + U8* buffer = getDiskCacheBuffer(size); if (!buffer) { - LL_WARNS(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL; - - // Not sure what size is reasonable for skin info, - // but if 20MB allocation failed, we definetely have issues - const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB - if (size < MAX_SIZE) - { - LLAppViewer::instance()->outOfMemorySoftQuit(); - } // else ignore failures for anomalously large data - LLMutexLock locker(mMutex); mSkinUnavailableQ.emplace_back(mesh_id); return true; } @@ -1443,12 +1470,9 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) { //attempt to parse if (skinInfoReceived(mesh_id, buffer, size)) { - delete[] buffer; return true; } } - - delete[] buffer; } //reading from cache failed for whatever reason, fetch from sim @@ -1537,18 +1561,9 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (in_cache && file.getSize() >= disk_ofset + size) { - U8* buffer = new(std::nothrow) U8[size]; + U8* buffer = getDiskCacheBuffer(size); if (!buffer) { - LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL; - - // Not sure what size is reasonable for decomposition - // but if 20MB allocation failed, we definetely have issues - const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB - if (size < MAX_SIZE) - { - LLAppViewer::instance()->outOfMemorySoftQuit(); - } // else ignore failures for anomalously large decompositiions return true; } LLMeshRepository::sCacheBytesRead += size; @@ -1568,12 +1583,9 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) { //attempt to parse if (decompositionReceived(mesh_id, buffer, size)) { - delete[] buffer; return true; } } - - delete[] buffer; } //reading from cache failed for whatever reason, fetch from sim @@ -1650,21 +1662,13 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) { LLMeshRepository::sCacheBytesRead += size; ++LLMeshRepository::sCacheReads; - file.seek(disk_ofset); - U8* buffer = new(std::nothrow) U8[size]; + + U8* buffer = getDiskCacheBuffer(size); if (!buffer) { - LL_WARNS(LOG_MESH) << "Failed to allocate memory for mesh decomposition, size: " << size << LL_ENDL; - - // Not sure what size is reasonable for physcis - // but if 20MB allocation failed, we definetely have issues - const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB - if (size < MAX_SIZE) - { - LLAppViewer::instance()->outOfMemorySoftQuit(); - } // else ignore failures for anomalously large data return true; } + file.seek(disk_ofset); file.read(buffer, size); //make sure buffer isn't all 0's by checking the first 1KB (reserved block but not written) @@ -1678,12 +1682,9 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) { //attempt to parse if (physicsShapeReceived(mesh_id, buffer, size) == MESH_OK) { - delete[] buffer; return true; } } - - delete[] buffer; } //reading from cache failed for whatever reason, fetch from sim @@ -1885,18 +1886,9 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (in_cache && file.getSize() >= disk_ofset + size) { - U8* buffer = new(std::nothrow) U8[size]; + U8* buffer = getDiskCacheBuffer(size); if (!buffer) { - LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL; - - // Not sure what size is reasonable for a mesh, - // but if 20MB allocation failed, we definetely have issues - const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB - if (size < MAX_SIZE) - { - LLAppViewer::instance()->outOfMemorySoftQuit(); - } // else ignore failures for anomalously large data LLMutexLock lock(mMutex); mUnavailableQ.push_back(LODRequest(mesh_params, lod)); return true; @@ -1917,15 +1909,11 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, { //attempt to parse if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK) { - delete[] buffer; - LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL; return true; } } - - delete[] buffer; } //reading from cache failed for whatever reason, fetch from sim -- cgit v1.2.3 From 314ef5389cb57646fc20971335e65b6a8dab7235 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 31 Jan 2025 19:09:05 +0200 Subject: #3488 Fix last retry not saving handle Request was sent and handle is valid, save it --- indra/newview/llmeshrepository.cpp | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 821288429d..186e4322f6 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -963,7 +963,7 @@ void LLMeshRepoThread::run() { incomplete.emplace_back(req); } - else if (!fetchMeshSkinInfo(req.mId, req.canRetry())) + else if (!fetchMeshSkinInfo(req.mId)) { if (req.canRetry()) { @@ -1010,7 +1010,7 @@ void LLMeshRepoThread::run() // failed to load before, wait a bit incomplete.push_front(req); } - else if (!fetchMeshLOD(req.mMeshParams, req.mLOD, req.canRetry())) + else if (!fetchMeshLOD(req.mMeshParams, req.mLOD)) { if (req.canRetry()) { @@ -1058,7 +1058,7 @@ void LLMeshRepoThread::run() // failed to load before, wait a bit incomplete.push_front(req); } - else if (!fetchMeshHeader(req.mMeshParams, req.canRetry())) + else if (!fetchMeshHeader(req.mMeshParams)) { if (req.canRetry()) { @@ -1410,7 +1410,7 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, } -bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) +bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) { LL_PROFILE_ZONE_SCOPED; if (!mHeaderMutex) @@ -1491,16 +1491,11 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry) << LL_ENDL; ret = false; } - else if(can_retry) + else { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); } - else - { - LLMutexLock locker(mMutex); - mSkinUnavailableQ.emplace_back(mesh_id); - } } else { @@ -1767,7 +1762,7 @@ void LLMeshRepoThread::decActiveSkinRequests() } //return false if failed to get header -bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry) +bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) { LL_PROFILE_ZONE_SCOPED; ++LLMeshRepository::sMeshRequestCount; @@ -1838,7 +1833,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c << LL_ENDL; retval = false; } - else if (can_retry) + else { handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); @@ -1849,7 +1844,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c } //return false if failed to get mesh lod. -bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry) +bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { LL_PROFILE_ZONE_SCOPED; if (!mHeaderMutex) @@ -1934,16 +1929,11 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, << LL_ENDL; retval = false; } - else if (can_retry) + else { + // we already made a request, store the handle handler->mHttpHandle = handle; mHttpRequestSet.insert(handler); - // *NOTE: Allowing a re-request, not marking as unavailable. Is that correct? - } - else - { - LLMutexLock lock(mMutex); - mUnavailableQ.push_back(LODRequest(mesh_params, lod)); } } else -- cgit v1.2.3 From 33303b9011c8be0dae46a3005fa2e89ee5d64ef9 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 31 Jan 2025 21:44:57 +0200 Subject: #3488 Split mesh thread mutexes Tracy shows that thread waits a lot on mutexes. Split in and out mutexes, so that new work can be scheduled by main thread without blocking handling --- indra/newview/llmeshrepository.cpp | 95 +++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 38 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 186e4322f6..e8f86e17ea 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -255,12 +255,12 @@ // mSkinInfoQ mMutex rw.repo.mMutex, rw.main.mMutex [5] (was: [0]) // mDecompositionRequests mMutex rw.repo.mMutex, ro.repo.none [5] // mPhysicsShapeRequests mMutex rw.repo.mMutex, ro.repo.none [5] -// mDecompositionQ mMutex rw.repo.mMutex, rw.main.mMutex [5] (was: [0]) +// mDecompositionQ mMutex rw.repo.mLoadedMutex, rw.main.mLoadedMutex [5] (was: [0]) // mHeaderReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex // mLODReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex -// mUnavailableQ mMutex rw.repo.none [0], ro.main.none [5], rw.main.mMutex -// mLoadedQ mMutex rw.repo.mMutex, ro.main.none [5], rw.main.mMutex -// mPendingLOD mMutex rw.repo.mMutex, rw.any.mMutex +// mUnavailableQ mMutex rw.repo.none [0], ro.main.none [5], rw.main.mLoadedMutex +// mLoadedQ mMutex rw.repo.mLoadedMutex, ro.main.none [5], rw.main.mLoadedMutex +// mPendingLOD mMutex rw.repo.mPendingMutex, rw.any.mPendingMutex // mGetMeshCapability mMutex rw.main.mMutex, ro.repo.mMutex (was: [0]) // mGetMesh2Capability mMutex rw.main.mMutex, ro.repo.mMutex (was: [0]) // mGetMeshVersion mMutex rw.main.mMutex, ro.repo.mMutex @@ -850,6 +850,8 @@ LLMeshRepoThread::LLMeshRepoThread() mMutex = new LLMutex(); mHeaderMutex = new LLMutex(); + mLoadedMutex = new LLMutex(); + mPendingMutex = new LLMutex(); mSignal = new LLCondition(); mHttpRequest = new LLCore::HttpRequest; mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); @@ -893,6 +895,10 @@ LLMeshRepoThread::~LLMeshRepoThread() mMutex = NULL; delete mHeaderMutex; mHeaderMutex = NULL; + delete mLoadedMutex; + mLoadedMutex = NULL; + delete mPendingMutex; + mPendingMutex = NULL; delete mSignal; mSignal = NULL; delete[] mDiskCacheBuffer; @@ -972,7 +978,7 @@ void LLMeshRepoThread::run() } else { - LLMutexLock locker(mMutex); + LLMutexLock locker(mLoadedMutex); mSkinUnavailableQ.push_back(req); LL_DEBUGS() << "mSkinReqQ failed: " << req.mId << LL_ENDL; } @@ -1021,7 +1027,7 @@ void LLMeshRepoThread::run() else { // too many fails - LLMutexLock lock(mMutex); + LLMutexLock lock(mLoadedMutex); mUnavailableQ.push_back(req); LL_WARNS() << "Failed to load " << req.mMeshParams << " , skip" << LL_ENDL; } @@ -1217,15 +1223,11 @@ void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { //could be called from any thread const LLUUID& mesh_id = mesh_params.getSculptID(); - LLMutexLock lock(mMutex); - LLMutexLock header_lock(mHeaderMutex); loadMeshLOD(mesh_id, mesh_params, lod); } void LLMeshRepoThread::loadMeshLODs(const lod_list_t& list) { //could be called from any thread - LLMutexLock lock(mMutex); - LLMutexLock header_lock(mHeaderMutex); for (auto lod_pair : list) { const LLVolumeParams& mesh_params = lod_pair.first; @@ -1237,19 +1239,25 @@ void LLMeshRepoThread::loadMeshLODs(const lod_list_t& list) void LLMeshRepoThread::loadMeshLOD(const LLUUID& mesh_id, const LLVolumeParams& mesh_params, S32 lod) { - // must be mutex locked by caller - mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); - if (iter != mMeshHeader.end()) + bool has_header = false; + { + LLMutexLock header_lock(mHeaderMutex); + mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); + has_header = iter != mMeshHeader.end(); + } + if (has_header) { //if we have the header, request LOD byte range LODRequest req(mesh_params, lod); { + LLMutexLock lock(mMutex); mLODReqQ.push(req); LLMeshRepository::sLODProcessing++; } } else { + LLMutexLock lock(mPendingMutex); HeaderRequest req(mesh_params); pending_lod_map::iterator pending = mPendingLOD.find(mesh_id); @@ -1269,10 +1277,12 @@ void LLMeshRepoThread::loadMeshLOD(const LLUUID& mesh_id, const LLVolumeParams& else { //if no header request is pending, fetch header - mHeaderReqQ.push(req); auto& array = mPendingLOD[mesh_id]; std::fill(array.begin(), array.end(), 0); array[lod]++; + + LLMutexLock lock(mMutex); + mHeaderReqQ.push(req); } } } @@ -1451,6 +1461,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) U8* buffer = getDiskCacheBuffer(size); if (!buffer) { + LLMutexLock locker(mLoadedMutex); mSkinUnavailableQ.emplace_back(mesh_id); return true; } @@ -1499,13 +1510,13 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) } else { - LLMutexLock locker(mMutex); + LLMutexLock locker(mLoadedMutex); mSkinUnavailableQ.emplace_back(mesh_id); } } else { - LLMutexLock locker(mMutex); + LLMutexLock locker(mLoadedMutex); mSkinUnavailableQ.emplace_back(mesh_id); } } @@ -1884,7 +1895,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) U8* buffer = getDiskCacheBuffer(size); if (!buffer) { - LLMutexLock lock(mMutex); + LLMutexLock lock(mLoadedMutex); mUnavailableQ.push_back(LODRequest(mesh_params, lod)); return true; } @@ -1938,13 +1949,13 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } else { - LLMutexLock lock(mMutex); + LLMutexLock lock(mLoadedMutex); mUnavailableQ.push_back(LODRequest(mesh_params, lod)); } } else { - LLMutexLock lock(mMutex); + LLMutexLock lock(mLoadedMutex); mUnavailableQ.push_back(LODRequest(mesh_params, lod)); } } @@ -2079,7 +2090,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes std::array pending_lods; bool has_pending_lods = false; { - LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. + LLMutexLock lock(mPendingMutex); // make sure only one thread access mPendingLOD at the same time. pending_lod_map::iterator iter = mPendingLOD.find(mesh_id); if (iter != mPendingLOD.end()) { @@ -2158,7 +2169,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p LoadedMesh mesh(volume, mesh_params, lod); { - LLMutexLock lock(mMutex); + LLMutexLock lock(mLoadedMutex); mLoadedQ.push_back(mesh); // LLPointer is not thread safe, since we added this pointer into // threaded list, make sure counter gets decreased inside mutex lock @@ -2216,7 +2227,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat { // Move the LLPointer in to the skin info queue to avoid reference // count modification after we leave the lock - LLMutexLock lock(mMutex); + LLMutexLock lock(mLoadedMutex); mSkinInfoQ.emplace_back(std::move(info)); } } @@ -2253,7 +2264,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3 LLModel::Decomposition* d = new LLModel::Decomposition(decomp); d->mMeshID = mesh_id; { - LLMutexLock lock(mMutex); + LLMutexLock lock(mLoadedMutex); mDecompositionQ.push_back(d); } } @@ -2303,7 +2314,7 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_ } { - LLMutexLock lock(mMutex); + LLMutexLock lock(mLoadedMutex); mDecompositionQ.push_back(d); } return MESH_OK; @@ -3101,11 +3112,11 @@ void LLMeshRepoThread::notifyLoadedMeshes() { std::deque loaded_queue; - mMutex->lock(); + mLoadedMutex->lock(); if (!mLoadedQ.empty()) { loaded_queue.swap(mLoadedQ); - mMutex->unlock(); + mLoadedMutex->unlock(); update_metrics = true; @@ -3123,17 +3134,21 @@ void LLMeshRepoThread::notifyLoadedMeshes() } } } + else + { + mLoadedMutex->unlock(); + } } if (!mUnavailableQ.empty()) { std::deque unavil_queue; - mMutex->lock(); + mLoadedMutex->lock(); if (!mUnavailableQ.empty()) { unavil_queue.swap(mUnavailableQ); - mMutex->unlock(); + mLoadedMutex->unlock(); update_metrics = true; @@ -3143,11 +3158,15 @@ void LLMeshRepoThread::notifyLoadedMeshes() gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); } } + else + { + mLoadedMutex->unlock(); + } } if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || ! mDecompositionQ.empty()) { - if (mMutex->trylock()) + if (mLoadedMutex->trylock()) { std::deque> skin_info_q; std::deque skin_info_unavail_q; @@ -3168,7 +3187,7 @@ void LLMeshRepoThread::notifyLoadedMeshes() decomp_q.swap(mDecompositionQ); } - mMutex->unlock(); + mLoadedMutex->unlock(); // Process the elements free of the lock while (! skin_info_q.empty()) @@ -3409,7 +3428,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) << LL_ENDL; // Can't get the header so none of the LODs will be available - LLMutexLock lock(gMeshRepo.mThread->mMutex); + LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) { gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); @@ -3439,7 +3458,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b << LL_ENDL; // Can't get the header so none of the LODs will be available - LLMutexLock lock(gMeshRepo.mThread->mMutex); + LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) { gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); @@ -3521,7 +3540,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b gMeshRepo.mThread->mHeaderMutex->unlock(); // headerReceived() parsed header, but header's data is invalid so none of the LODs will be available - LLMutexLock lock(gMeshRepo.mThread->mMutex); + LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) { gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); @@ -3550,7 +3569,7 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status) << " (" << status.toTerseString() << "). Not retrying." << LL_ENDL; - LLMutexLock lock(gMeshRepo.mThread->mMutex); + LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); } @@ -3610,7 +3629,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body << " Data size: " << data_size << " Not retrying." << LL_ENDL; - LLMutexLock lock(gMeshRepo.mThread->mMutex); + LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); } } @@ -3621,7 +3640,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body << " LOD: " << mLOD << " Data size: " << data_size << LL_ENDL; - LLMutexLock lock(gMeshRepo.mThread->mMutex); + LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); } } @@ -3641,7 +3660,7 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) << ", Reason: " << status.toString() << " (" << status.toTerseString() << "). Not retrying." << LL_ENDL; - LLMutexLock lock(gMeshRepo.mThread->mMutex); + LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); } @@ -3697,7 +3716,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* LL_WARNS(LOG_MESH) << "Error during mesh skin info processing. ID: " << mMeshID << ", Unknown reason. Not retrying." << LL_ENDL; - LLMutexLock lock(gMeshRepo.mThread->mMutex); + LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); } } -- cgit v1.2.3 From 7eb9dd3e0d744406db4006f42ee217a67ab3c6e0 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 01:33:29 +0200 Subject: #3488 Move lod processing out of mesh thread --- indra/newview/llmeshrepository.cpp | 217 ++++++++++++++++++++++++++++--------- 1 file changed, 164 insertions(+), 53 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index e8f86e17ea..347e7fde70 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -388,12 +388,12 @@ U32 LLMeshRepository::sLODProcessing = 0; U32 LLMeshRepository::sLODPending = 0; U32 LLMeshRepository::sCacheBytesRead = 0; -U32 LLMeshRepository::sCacheBytesWritten = 0; +std::atomic LLMeshRepository::sCacheBytesWritten = 0; U32 LLMeshRepository::sCacheBytesHeaders = 0; U32 LLMeshRepository::sCacheBytesSkins = 0; U32 LLMeshRepository::sCacheBytesDecomps = 0; U32 LLMeshRepository::sCacheReads = 0; -U32 LLMeshRepository::sCacheWrites = 0; +std::atomic LLMeshRepository::sCacheWrites = 0; U32 LLMeshRepository::sMaxLockHoldoffs = 0; LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, false); // true -> gather cpu metrics @@ -587,6 +587,7 @@ public: : LLCore::HttpHandler(), mMeshParams(), mProcessed(false), + mHasDataOwnership(true), mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), mOffset(offset), mRequestedBytes(requested_bytes) @@ -610,6 +611,9 @@ public: LLCore::HttpHandle mHttpHandle; U32 mOffset; U32 mRequestedBytes; + +protected: + bool mHasDataOwnership = true; }; @@ -662,6 +666,9 @@ public: virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); virtual void processFailure(LLCore::HttpStatus status); +private: + void processLod(U8* data, S32 data_size); + public: S32 mLOD; }; @@ -864,6 +871,11 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_VND_LL_MESH); mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH2); mHttpLargePolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_LARGE_MESH); + + // Lod processing is expensive due to the number of requests + // and a need to do expensive cacheOptimize(). + mLodThreadPool.reset(new LL::ThreadPool("MeshLodProcessing", 2)); + mLodThreadPool->start(); } @@ -1892,9 +1904,19 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (in_cache && file.getSize() >= disk_ofset + size) { - U8* buffer = getDiskCacheBuffer(size); + U8* buffer = new(std::nothrow) U8[size]; if (!buffer) { + LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL; + + // Not sure what size is reasonable for a mesh, + // but if 20MB allocation failed, we definetely have issues + const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB + if (size < MAX_SIZE) + { + LLAppViewer::instance()->outOfMemorySoftQuit(); + } // else ignore failures for anomalously large data + LLMutexLock lock(mLoadedMutex); mUnavailableQ.push_back(LODRequest(mesh_params, lod)); return true; @@ -1912,14 +1934,75 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } if (!zero) - { //attempt to parse - if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK) + { + //attempt to parse + bool posted = mLodThreadPool->getQueue().post( + [mesh_params, mesh_id, lod, buffer, size] + () { + if (gMeshRepo.mThread->lodReceived(mesh_params, lod, buffer, size) == MESH_OK) + { + LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL; + } + else + { + // either header is faulty or something else overwrote the cache + S32 header_size = 0; + U32 header_flags = 0; + { + LL_WARNS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL; + + LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + + auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id); + if (header_it == gMeshRepo.mThread->mMeshHeader.end()) + { + LLMeshHeader& header = header_it->second; + // for safety just mark everything as missing + header.mSkinInCache = false; + header.mPhysicsConvexInCache = false; + header.mPhysicsMeshInCache = false; + for (S32 i = 0; i < LLModel::NUM_LODS; ++i) + { + header.mLodInCache[i] = false; + } + header_size = header.mHeaderSize; + header_flags = header.getFlags(); + } + } + + S32 bytes = header_size + CACHE_PREAMBLE_SIZE; + LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + if (file.getMaxSize() >= bytes) + { + write_preamble(file, header_size, header_flags); + } + + { + LLMutexLock lock(gMeshRepo.mThread->mMutex); + LODRequest req(mesh_params, lod); + gMeshRepo.mThread->mLODReqQ.push(req); + } + } + delete[] buffer; + }); + + if (posted) + { + // now lambda owns buffer + return true; + } + else if (lodReceived(mesh_params, lod, buffer, size) == MESH_OK) + { + delete[] buffer; LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL; return true; } + } + + delete[] buffer; } //reading from cache failed for whatever reason, fetch from sim @@ -2130,6 +2213,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes } if (request_lod) { + LLMutexLock lock(mMutex); LODRequest req(mesh_params, i); mLODReqQ.push(req); LLMeshRepository::sLODProcessing++; @@ -2144,7 +2228,6 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size) { - LL_PROFILE_ZONE_SCOPED; if (data == NULL || data_size == 0) { return MESH_NO_DATA; @@ -3395,7 +3478,10 @@ void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRespo processData(body, body_offset, data, static_cast(data_size) - body_offset); - delete [] data; + if (mHasDataOwnership) + { + delete [] data; + } } // Release handler @@ -3572,65 +3658,89 @@ void LLMeshLODHandler::processFailure(LLCore::HttpStatus status) LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); } - -void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, - U8 * data, S32 data_size) +void LLMeshLODHandler::processLod(U8* data, S32 data_size) { - LL_PROFILE_ZONE_SCOPED; - if ((!MESH_LOD_PROCESS_FAILED) - && ((data != NULL) == (data_size > 0))) // if we have data but no size or have size but no data, something is wrong + EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size); + if (result == MESH_OK) { - EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size); - if (result == MESH_OK) - { - // good fetch from sim, write to cache - LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + // good fetch from sim, write to cache + LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - S32 offset = mOffset + CACHE_PREAMBLE_SIZE; - S32 size = mRequestedBytes; + S32 offset = mOffset + CACHE_PREAMBLE_SIZE; + S32 size = mRequestedBytes; - if (file.getSize() >= offset+size) + if (file.getSize() >= offset + size) + { + S32 header_bytes = 0; + U32 flags = 0; { - S32 header_bytes = 0; - U32 flags = 0; - { - LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); - LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); - if (header_it != gMeshRepo.mThread->mMeshHeader.end()) + LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); + if (header_it != gMeshRepo.mThread->mMeshHeader.end()) + { + LLMeshHeader& header = header_it->second; + // update header + if (!header.mLodInCache[mLOD]) { - LLMeshHeader& header = header_it->second; - // update header - if (!header.mLodInCache[mLOD]) - { - header.mLodInCache[mLOD] = true; - header_bytes = header.mHeaderSize; - flags = header.getFlags(); - } - // todo: handle else because we shouldn't have requested twice? + header.mLodInCache[mLOD] = true; + header_bytes = header.mHeaderSize; + flags = header.getFlags(); } + // todo: handle else because we shouldn't have requested twice? } - if (flags > 0) - { - write_preamble(file, header_bytes, flags); - } - - file.seek(offset); - file.write(data, size); - LLMeshRepository::sCacheBytesWritten += size; - ++LLMeshRepository::sCacheWrites; } + if (flags > 0) + { + write_preamble(file, header_bytes, flags); + } + + file.seek(offset); + file.write(data, size); + LLMeshRepository::sCacheBytesWritten += size; + ++LLMeshRepository::sCacheWrites; + } + } + else + { + LL_WARNS(LOG_MESH) << "Error during mesh LOD processing. ID: " << mMeshParams.getSculptID() + << ", Reason: " << result + << " LOD: " << mLOD + << " Data size: " << data_size + << " Not retrying." + << LL_ENDL; + LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); + gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); + } +} + +void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, + U8 * data, S32 data_size) +{ + LL_PROFILE_ZONE_SCOPED; + if ((!MESH_LOD_PROCESS_FAILED) + && ((data != NULL) == (data_size > 0))) // if we have data but no size or have size but no data, something is wrong + { + LLMeshHandlerBase::ptr_t shrd_handler = shared_from_this(); + bool posted = gMeshRepo.mThread->mLodThreadPool->getQueue().post( + [shrd_handler, data, data_size] + () + { + LLMeshLODHandler* handler = (LLMeshLODHandler * )shrd_handler.get(); + handler->processLod(data, data_size); + delete[] data; + }); + + if (posted) + { + // ownership of data was passed to the lambda + mHasDataOwnership = false; } else { - LL_WARNS(LOG_MESH) << "Error during mesh LOD processing. ID: " << mMeshParams.getSculptID() - << ", Reason: " << result - << " LOD: " << mLOD - << " Data size: " << data_size - << " Not retrying." - << LL_ENDL; - LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); - gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); + // mesh thread dies later than event queue, so this is normal + LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mLodThreadPool" << LL_ENDL; + processLod(data, data_size); } } else @@ -3917,6 +4027,7 @@ void LLMeshRepository::shutdown() } mThread->mSignal->broadcast(); + mThread->mLodThreadPool->close(); while (!mThread->isStopped()) { -- cgit v1.2.3 From f5f98e1c285f826f0a20af6188f2fcd1d7c1fe6e Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 02:29:10 +0200 Subject: #3488 Fix mesh header condition --- indra/newview/llmeshrepository.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 347e7fde70..6f95544503 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1820,8 +1820,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) } U32 flags = 0; memcpy(&flags, buffer + 2 * sizeof(U32), sizeof(U32)); - // Todo: parse and pass flags, they are the reason for the preamble - if (headerReceived(mesh_params, buffer + CACHE_PREAMBLE_SIZE, bytes, flags) == MESH_OK) + if (headerReceived(mesh_params, buffer + CACHE_PREAMBLE_SIZE, bytes - CACHE_PREAMBLE_SIZE, flags) == MESH_OK) { LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL; @@ -1902,9 +1901,9 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE; //check cache for mesh asset LLFileSystem file(mesh_id, LLAssetType::AT_MESH); - if (in_cache && file.getSize() >= disk_ofset + size) + if (in_cache && (file.getSize() >= disk_ofset + size)) { - U8* buffer = new(std::nothrow) U8[size]; + U8* buffer = new(std::nothrow) U8[size]; // todo, make buffer thread local and read in thread? if (!buffer) { LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL; @@ -1955,7 +1954,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id); - if (header_it == gMeshRepo.mThread->mMeshHeader.end()) + if (header_it != gMeshRepo.mThread->mMeshHeader.end()) { LLMeshHeader& header = header_it->second; // for safety just mark everything as missing @@ -1971,11 +1970,13 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } } - S32 bytes = header_size + CACHE_PREAMBLE_SIZE; - LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - if (file.getMaxSize() >= bytes) + if (header_size > 0) { - write_preamble(file, header_size, header_flags); + LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + if (file.getMaxSize() >= CACHE_PREAMBLE_SIZE) + { + write_preamble(file, header_size, header_flags); + } } { -- cgit v1.2.3 From 587444bd279e7505a50ee9f2bbbe603c96d0214d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 02:29:10 +0200 Subject: #3488 mutex lock mSkinMap --- indra/newview/llmeshrepository.cpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 6f95544503..edffaae8b5 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -859,6 +859,7 @@ LLMeshRepoThread::LLMeshRepoThread() mHeaderMutex = new LLMutex(); mLoadedMutex = new LLMutex(); mPendingMutex = new LLMutex(); + mSkinMapMutex = new LLMutex(); mSignal = new LLCondition(); mHttpRequest = new LLCore::HttpRequest; mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); @@ -902,19 +903,21 @@ LLMeshRepoThread::~LLMeshRepoThread() } delete mHttpRequest; - mHttpRequest = NULL; + mHttpRequest = nullptr; delete mMutex; - mMutex = NULL; + mMutex = nullptr; delete mHeaderMutex; - mHeaderMutex = NULL; + mHeaderMutex = nullptr; delete mLoadedMutex; - mLoadedMutex = NULL; + mLoadedMutex = nullptr; delete mPendingMutex; - mPendingMutex = NULL; + mPendingMutex = nullptr; + delete mSkinMapMutex; + mSkinMapMutex = nullptr; delete mSignal; - mSignal = NULL; + mSignal = nullptr; delete[] mDiskCacheBuffer; - mDiskCacheBuffer = NULL; + mDiskCacheBuffer = nullptr; } void LLMeshRepoThread::run() @@ -1949,7 +1952,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) S32 header_size = 0; U32 header_flags = 0; { - LL_WARNS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL; + LL_DEBUGS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL; LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); @@ -2240,8 +2243,12 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p if (volume->getNumFaces() > 0) { // if we have a valid SkinInfo, cache per-joint bounding boxes for this LOD - LLMeshSkinInfo* skin_info = mSkinMap[mesh_params.getSculptID()]; - if (skin_info && isAgentAvatarValid()) + LLPointer skin_info = nullptr; + { + LLMutexLock lock(mSkinMapMutex); + skin_info = mSkinMap[mesh_params.getSculptID()]; + } + if (skin_info.notNull() && isAgentAvatarValid()) { for (S32 i = 0; i < volume->getNumFaces(); ++i) { @@ -2306,7 +2313,10 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat // copy the skin info for the background thread so we can use it // to calculate per-joint bounding boxes when volumes are loaded - mSkinMap[mesh_id] = new LLMeshSkinInfo(*info); + { + LLMutexLock lock(mSkinMapMutex); + mSkinMap[mesh_id] = new LLMeshSkinInfo(*info); + } { // Move the LLPointer in to the skin info queue to avoid reference @@ -4349,6 +4359,7 @@ void LLMeshRepository::notifyLoadedMeshes() // erase from background thread mThread->mWorkQueue.post([=]() { + LLMutexLock(mThread->mSkinMapMutex); mThread->mSkinMap.erase(id); }); } -- cgit v1.2.3 From 912c99ae96ce10da5916d24b5339f6bb2409dac6 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 04:18:14 +0200 Subject: #3488 Fix cache using wrong offset --- indra/newview/llmeshrepository.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index edffaae8b5..6c51ac23ac 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3706,7 +3706,7 @@ void LLMeshLODHandler::processLod(U8* data, S32 data_size) write_preamble(file, header_bytes, flags); } - file.seek(offset); + file.seek(offset, 0); file.write(data, size); LLMeshRepository::sCacheBytesWritten += size; ++LLMeshRepository::sCacheWrites; @@ -3828,7 +3828,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* write_preamble(file, header_bytes, flags); } - file.seek(offset); + file.seek(offset, 0); file.write(data, size); } } @@ -3903,7 +3903,7 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S write_preamble(file, header_bytes, flags); } - file.seek(offset); + file.seek(offset, 0); file.write(data, size); } } @@ -3976,7 +3976,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 write_preamble(file, header_bytes, flags); } - file.seek(offset); + file.seek(offset, 0); file.write(data, size); } } -- cgit v1.2.3 From 8c4abf8feb599a3c8f90019a2953a672efd3a09b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 08:01:53 +0200 Subject: #3488 Fix skin info not reading from cache --- indra/newview/llmeshrepository.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 6c51ac23ac..955558e56e 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3809,7 +3809,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* { LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); - LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); + LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); if (header_it != gMeshRepo.mThread->mMeshHeader.end()) { LLMeshHeader& header = header_it->second; @@ -3884,7 +3884,7 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S { LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); - LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); + LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); if (header_it != gMeshRepo.mThread->mMeshHeader.end()) { LLMeshHeader& header = header_it->second; @@ -3957,7 +3957,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 { LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); - LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); + LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); if (header_it != gMeshRepo.mThread->mMeshHeader.end()) { LLMeshHeader& header = header_it->second; -- cgit v1.2.3 From 9bfdb7cf6ce05f4c2f97f82888a9aaae62298c42 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 08:32:38 +0200 Subject: #3488 #3488 Move skin info processing out of mesh thread Skin info parsing is expensive, offload to thread pool --- indra/newview/llmeshrepository.cpp | 138 ++++++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 18 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 955558e56e..699bec539c 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -693,6 +693,8 @@ protected: LLMeshSkinInfoHandler(const LLMeshSkinInfoHandler &); // Not defined void operator=(const LLMeshSkinInfoHandler &); // Not defined + void processSkin(U8* data, S32 data_size); + public: virtual void processData(LLCore::BufferArray * body, S32 body_offset, U8 * data, S32 data_size); virtual void processFailure(LLCore::HttpStatus status); @@ -875,8 +877,8 @@ LLMeshRepoThread::LLMeshRepoThread() // Lod processing is expensive due to the number of requests // and a need to do expensive cacheOptimize(). - mLodThreadPool.reset(new LL::ThreadPool("MeshLodProcessing", 2)); - mLodThreadPool->start(); + mMeshThreadPool.reset(new LL::ThreadPool("MeshLodProcessing", 2)); + mMeshThreadPool->start(); } @@ -1319,7 +1321,7 @@ U8* LLMeshRepoThread::getDiskCacheBuffer(S32 size) mDiskCacheBuffer = NULL; // Not sure what size is reasonable - // but if 30MB allocation failed, we definetely have issues + // but if 30MB allocation failed, we definitely have issues const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB if (size < MAX_SIZE) { @@ -1473,9 +1475,18 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (in_cache && file.getSize() >= disk_ofset + size) { - U8* buffer = getDiskCacheBuffer(size); + U8* buffer = new(std::nothrow) U8[size]; if (!buffer) { + LL_WARNS(LOG_MESH) << "Failed to allocate memory for skin info, size: " << size << LL_ENDL; + + // Not sure what size is reasonable for skin info, + // but if 30MB allocation failed, we definitely have issues + const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB + if (size < MAX_SIZE) + { + LLAppViewer::instance()->outOfMemorySoftQuit(); + } // else ignore failures for anomalously large data LLMutexLock locker(mLoadedMutex); mSkinUnavailableQ.emplace_back(mesh_id); return true; @@ -1493,12 +1504,68 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) } if (!zero) - { //attempt to parse - if (skinInfoReceived(mesh_id, buffer, size)) + { + //attempt to parse + bool posted = mMeshThreadPool->getQueue().post( + [mesh_id, buffer, size] + () + { + if (!gMeshRepo.mThread->skinInfoReceived(mesh_id, buffer, size)) + { + // either header is faulty or something else overwrote the cache + S32 header_size = 0; + U32 header_flags = 0; + { + LL_DEBUGS(LOG_MESH) << "Mesh header for ID " << mesh_id << " cache mismatch." << LL_ENDL; + + LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); + + auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id); + if (header_it != gMeshRepo.mThread->mMeshHeader.end()) + { + LLMeshHeader& header = header_it->second; + // for safety just mark everything as missing + header.mSkinInCache = false; + header.mPhysicsConvexInCache = false; + header.mPhysicsMeshInCache = false; + for (S32 i = 0; i < LLModel::NUM_LODS; ++i) + { + header.mLodInCache[i] = false; + } + header_size = header.mHeaderSize; + header_flags = header.getFlags(); + } + } + + if (header_size > 0) + { + LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + if (file.getMaxSize() >= CACHE_PREAMBLE_SIZE) + { + write_preamble(file, header_size, header_flags); + } + } + + { + LLMutexLock lock(gMeshRepo.mThread->mMutex); + UUIDBasedRequest req(mesh_id); + gMeshRepo.mThread->mSkinRequests.push_back(req); + } + } + delete[] buffer; + }); + if (posted) { + // lambda owns buffer + return true; + } + else if (skinInfoReceived(mesh_id, buffer, size)) + { + delete[] buffer; return true; } } + delete[] buffer; } //reading from cache failed for whatever reason, fetch from sim @@ -1912,7 +1979,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL; // Not sure what size is reasonable for a mesh, - // but if 20MB allocation failed, we definetely have issues + // but if 30MB allocation failed, we definitely have issues const S32 MAX_SIZE = 30 * 1024 * 1024; //30MB if (size < MAX_SIZE) { @@ -1938,7 +2005,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) if (!zero) { //attempt to parse - bool posted = mLodThreadPool->getQueue().post( + bool posted = mMeshThreadPool->getQueue().post( [mesh_params, mesh_id, lod, buffer, size] () { @@ -3733,7 +3800,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body && ((data != NULL) == (data_size > 0))) // if we have data but no size or have size but no data, something is wrong { LLMeshHandlerBase::ptr_t shrd_handler = shared_from_this(); - bool posted = gMeshRepo.mThread->mLodThreadPool->getQueue().post( + bool posted = gMeshRepo.mThread->mMeshThreadPool->getQueue().post( [shrd_handler, data, data_size] () { @@ -3750,7 +3817,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body else { // mesh thread dies later than event queue, so this is normal - LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mLodThreadPool" << LL_ENDL; + LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mMeshThreadPool" << LL_ENDL; processLod(data, data_size); } } @@ -3785,13 +3852,9 @@ void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); } -void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, - U8 * data, S32 data_size) +void LLMeshSkinInfoHandler::processSkin(U8* data, S32 data_size) { - LL_PROFILE_ZONE_SCOPED; - if ((!MESH_SKIN_INFO_PROCESS_FAILED) - && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong - && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) + if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) { // good fetch from sim, write to cache LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); @@ -3799,7 +3862,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* S32 offset = mOffset + CACHE_PREAMBLE_SIZE; S32 size = mRequestedBytes; - if (file.getSize() >= offset+size) + if (file.getSize() >= offset + size) { LLMeshRepository::sCacheBytesWritten += size; ++LLMeshRepository::sCacheWrites; @@ -3833,6 +3896,45 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* } } else + { + LL_WARNS(LOG_MESH) << "Error during mesh skin info processing. ID: " << mMeshID + << ", Unknown reason. Not retrying." + << LL_ENDL; + LLMutexLock lock(gMeshRepo.mThread->mLoadedMutex); + gMeshRepo.mThread->mSkinUnavailableQ.emplace_back(mMeshID); + } +} + +void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, + U8 * data, S32 data_size) +{ + LL_PROFILE_ZONE_SCOPED; + if ((!MESH_SKIN_INFO_PROCESS_FAILED) + && ((data != NULL) == (data_size > 0))) // if we have data but no size or have size but no data, something is wrong + { + LLMeshHandlerBase::ptr_t shrd_handler = shared_from_this(); + bool posted = gMeshRepo.mThread->mMeshThreadPool->getQueue().post( + [shrd_handler, data, data_size] + () + { + LLMeshSkinInfoHandler* handler = (LLMeshSkinInfoHandler*)shrd_handler.get(); + handler->processSkin(data, data_size); + delete[] data; + }); + + if (posted) + { + // ownership of data was passed to the lambda + mHasDataOwnership = false; + } + else + { + // mesh thread dies later than event queue, so this is normal + LL_INFOS_ONCE(LOG_MESH) << "Failed to post work into mMeshThreadPool" << LL_ENDL; + processSkin(data, data_size); + } + } + else { LL_WARNS(LOG_MESH) << "Error during mesh skin info processing. ID: " << mMeshID << ", Unknown reason. Not retrying." @@ -4038,7 +4140,7 @@ void LLMeshRepository::shutdown() } mThread->mSignal->broadcast(); - mThread->mLodThreadPool->close(); + mThread->mMeshThreadPool->close(); while (!mThread->isStopped()) { -- cgit v1.2.3 From 74d2ed918dd185cbc47ed64f78be76ae6b94a60f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 21:11:43 +0200 Subject: #3488 Speed up nearby avatar loading after a tp --- indra/newview/llmeshrepository.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 699bec539c..fdc7520d4d 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -2313,7 +2313,11 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p LLPointer skin_info = nullptr; { LLMutexLock lock(mSkinMapMutex); - skin_info = mSkinMap[mesh_params.getSculptID()]; + skin_map::iterator iter = mSkinMap.find(mesh_params.getSculptID()); + if (iter != mSkinMap.end()) + { + skin_info = iter->second; + } } if (skin_info.notNull() && isAgentAvatarValid()) { -- cgit v1.2.3 From 483e85cbf31e08e3692d2fb267bdaacdd0ed38a4 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 7 Feb 2025 23:24:33 +0200 Subject: #3488 Prioritization adjustments --- indra/newview/llmeshrepository.cpp | 40 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index fdc7520d4d..0fef1e2b61 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -343,22 +343,22 @@ static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch"); LLMeshRepository gMeshRepo; -const U32 CACHE_PREAMBLE_VERSION = 1; -const S32 CACHE_PREAMBLE_SIZE = sizeof(U32) * 3; //version, header_size, flags -const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space +constexpr U32 CACHE_PREAMBLE_VERSION = 1; +constexpr S32 CACHE_PREAMBLE_SIZE = sizeof(U32) * 3; //version, header_size, flags +constexpr S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space -const S32 REQUEST2_HIGH_WATER_MIN = 32; // Limits for GetMesh2 regions -const S32 REQUEST2_HIGH_WATER_MAX = 100; -const S32 REQUEST2_LOW_WATER_MIN = 16; -const S32 REQUEST2_LOW_WATER_MAX = 50; +constexpr S32 REQUEST2_HIGH_WATER_MIN = 32; // Limits for GetMesh2 regions +constexpr S32 REQUEST2_HIGH_WATER_MAX = 100; +constexpr S32 REQUEST2_LOW_WATER_MIN = 16; +constexpr S32 REQUEST2_LOW_WATER_MAX = 50; -const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue -const long SMALL_MESH_XFER_TIMEOUT = 120L; // Seconds to complete xfer, small mesh downloads -const long LARGE_MESH_XFER_TIMEOUT = 600L; // Seconds to complete xfer, large downloads +constexpr U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21; // Size at which requests goes to narrow/slow queue +constexpr long SMALL_MESH_XFER_TIMEOUT = 120L; // Seconds to complete xfer, small mesh downloads +constexpr long LARGE_MESH_XFER_TIMEOUT = 600L; // Seconds to complete xfer, large downloads -const U32 DOWNLOAD_RETRY_LIMIT = 8; -const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds +constexpr U32 DOWNLOAD_RETRY_LIMIT = 8; +constexpr F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds // Would normally like to retry on uploads as some // retryable failures would be recoverable. Unfortunately, @@ -368,7 +368,7 @@ const F32 DOWNLOAD_RETRY_DELAY = 0.5f; // seconds // cap which then produces a 404 on retry destroying some // (occasionally) useful error information. We'll leave // upload retries to the user as in the past. SH-4667. -const long UPLOAD_RETRY_LIMIT = 0L; +constexpr long UPLOAD_RETRY_LIMIT = 0L; // Maximum mesh version to support. Three least significant digits are reserved for the minor version, // with major version changes indicating a format change that is not backwards compatible and should not @@ -376,7 +376,7 @@ const long UPLOAD_RETRY_LIMIT = 0L; // present, the version is 0.001. A viewer that can parse version 0.001 can also parse versions up to 0.999, // but not 1.0 (integer 1000). // See wiki at https://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format -const S32 MAX_MESH_VERSION = 999; +constexpr S32 MAX_MESH_VERSION = 999; U32 LLMeshRepository::sBytesReceived = 0; U32 LLMeshRepository::sMeshRequestCount = 0; @@ -1869,8 +1869,8 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) if (size > 0) { // *NOTE: if the header size is ever more than 4KB, this will break - const S32 DISK_MINIMAL_READ = 4096; - static thread_local U8 buffer[DISK_MINIMAL_READ * 2]; + constexpr S32 DISK_MINIMAL_READ = 4096; + U8 buffer[DISK_MINIMAL_READ * 2]; S32 bytes = llmin(size, DISK_MINIMAL_READ); LLMeshRepository::sCacheBytesRead += bytes; ++LLMeshRepository::sCacheReads; @@ -4337,6 +4337,12 @@ F32 calculate_score(LLVOVolume* object) const LLVector3* box = avatar->getLastAnimExtents(); LLVector3 diag = box[1] - box[0]; radius = diag.magVec(); + + if (!avatar->isSelf() && !avatar->hasFirstFullAttachmentData()) + { + // slightly deprioritize avatars that are still receiving data + radius *= 0.9f; + } } return radius / llmax(av_drawable->mDistanceWRTCamera, 1.f); } @@ -5385,7 +5391,7 @@ F32 LLMeshCostData::getEstTrisForStreamingCost() F32 charged_tris = mEstTrisByLOD[3]; F32 allowed_tris = mEstTrisByLOD[3]; - const F32 ENFORCE_FLOOR = 64.0f; + constexpr F32 ENFORCE_FLOOR = 64.0f; for (S32 i=2; i>=0; i--) { // How many tris can we have in this LOD without affecting land impact? -- cgit v1.2.3 From 29b13af84b6092f2368ef8ebfd6431003a963c10 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 22 Feb 2025 10:41:11 +0200 Subject: #3596 Ensure correct loading mesh gets checked and removed --- indra/newview/llmeshrepository.cpp | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index bbfe58765c..283e5e51da 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3290,12 +3290,11 @@ void LLMeshRepoThread::notifyLoadedMeshes() { if (mesh.mVolume->getNumVolumeFaces() > 0) { - gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); + gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume, mesh.mLOD); } else { - gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, - LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail())); + gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, mesh.mLOD); } } } @@ -4219,24 +4218,24 @@ void LLMeshRepository::unregisterMesh(LLVOVolume* vobj) } } -S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod) +S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 new_lod, S32 last_lod) { LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH); // Manage time-to-load metrics for mesh download operations. metricsProgress(1); - if (detail < 0 || detail >= LLVolumeLODGroup::NUM_LODS) + if (new_lod < 0 || new_lod >= LLVolumeLODGroup::NUM_LODS) { - return detail; + return new_lod; } { LLMutexLock lock(mMeshMutex); //add volume to list of loading meshes const auto& mesh_id = mesh_params.getSculptID(); - mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_id); - if (iter != mLoadingMeshes[detail].end()) + mesh_load_map::iterator iter = mLoadingMeshes[new_lod].find(mesh_id); + if (iter != mLoadingMeshes[new_lod].end()) { //request pending for this mesh, append volume id to list auto it = std::find(iter->second.begin(), iter->second.end(), vobj); if (it == iter->second.end()) { @@ -4246,8 +4245,8 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para else { //first request for this mesh - mLoadingMeshes[detail][mesh_id].push_back(vobj); - mPendingRequests.emplace_back(new PendingRequestLOD(mesh_params, detail)); + mLoadingMeshes[new_lod][mesh_id].push_back(vobj); + mPendingRequests.emplace_back(new PendingRequestLOD(mesh_params, new_lod)); LLMeshRepository::sLODPending++; } } @@ -4276,7 +4275,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para } //next, see what the next lowest LOD available might be - for (S32 i = detail-1; i >= 0; --i) + for (S32 i = new_lod -1; i >= 0; --i) { LLVolume* lod = group->refLOD(i); if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) @@ -4289,7 +4288,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para } //no lower LOD is a available, is a higher lod available? - for (S32 i = detail+1; i < LLVolumeLODGroup::NUM_LODS; ++i) + for (S32 i = new_lod+1; i < LLVolumeLODGroup::NUM_LODS; ++i) { LLVolume* lod = group->refLOD(i); if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) @@ -4303,7 +4302,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para } } - return detail; + return new_lod; } F32 calculate_score(LLVOVolume* object) @@ -4699,15 +4698,14 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom } } -void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume) +void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod) { //called from main thread - S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail()); //get list of objects waiting to be notified this mesh is loaded const auto& mesh_id = mesh_params.getSculptID(); - mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_id); + mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id); - if (volume && obj_iter != mLoadingMeshes[detail].end()) + if (volume && obj_iter != mLoadingMeshes[lod].end()) { //make sure target volume is still valid if (volume->getNumVolumeFaces() <= 0) @@ -4717,6 +4715,7 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol } { //update system volume + S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail()); LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail); if (sys_volume) { @@ -4740,7 +4739,7 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol } } - mLoadingMeshes[detail].erase(obj_iter); + mLoadingMeshes[lod].erase(obj_iter); LLViewerStatsRecorder::instance().meshLoaded(); } -- cgit v1.2.3 From 1503e84602f91bef31a5c414852e480805e613b4 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 22 Feb 2025 10:42:28 +0200 Subject: #3596 Fix mMutex in mesh thread --- indra/newview/llmeshrepository.cpp | 50 +++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 9 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 283e5e51da..18ca0798bc 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1244,25 +1244,57 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } void LLMeshRepoThread::loadMeshLODs(const lod_list_t& list) -{ //could be called from any thread +{ // expectes mMutex to be locked for (auto lod_pair : list) { const LLVolumeParams& mesh_params = lod_pair.first; const LLUUID& mesh_id = mesh_params.getSculptID(); S32 lod = lod_pair.second; - loadMeshLOD(mesh_id, mesh_params, lod); + + if (hasHeader(mesh_id)) + { //if we have the header, request LOD byte range + + LODRequest req(mesh_params, lod); + { + mLODReqQ.push(req); + LLMeshRepository::sLODProcessing++; + } + } + else + { + LLMutexLock lock(mPendingMutex); + HeaderRequest req(mesh_params); + pending_lod_map::iterator pending = mPendingLOD.find(mesh_id); + + if (pending != mPendingLOD.end()) + { + //append this lod request to existing header request + if (lod < LLModel::NUM_LODS && lod >= 0) + { + pending->second[lod]++; + } + else + { + LL_WARNS(LOG_MESH) << "Invalid LOD request: " << lod << "for mesh" << mesh_id << LL_ENDL; + } + llassert_msg(lod < LLModel::NUM_LODS, "Requested lod is out of bounds"); + } + else + { + //if no header request is pending, fetch header + auto& array = mPendingLOD[mesh_id]; + std::fill(array.begin(), array.end(), 0); + array[lod]++; + + mHeaderReqQ.push(req); + } + } } } void LLMeshRepoThread::loadMeshLOD(const LLUUID& mesh_id, const LLVolumeParams& mesh_params, S32 lod) { - bool has_header = false; - { - LLMutexLock header_lock(mHeaderMutex); - mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); - has_header = iter != mMeshHeader.end(); - } - if (has_header) + if (hasHeader(mesh_id)) { //if we have the header, request LOD byte range LODRequest req(mesh_params, lod); -- cgit v1.2.3 From f75968a32878a56d579b911a743f6d7842e6593b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 22 Feb 2025 10:43:35 +0200 Subject: #3596 Convert some mesh repository functions to const --- indra/newview/llmeshrepository.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 18ca0798bc..bb699031d4 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -4972,7 +4972,7 @@ bool LLMeshRepository::hasSkinInfo(const LLUUID& mesh_id) return false; } -bool LLMeshRepository::hasHeader(const LLUUID& mesh_id) +bool LLMeshRepository::hasHeader(const LLUUID& mesh_id) const { if (mesh_id.isNull()) { @@ -4982,13 +4982,13 @@ bool LLMeshRepository::hasHeader(const LLUUID& mesh_id) return mThread->hasHeader(mesh_id); } -bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) +bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) const { LLMutexLock lock(mHeaderMutex); - mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); + mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id); if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0) { - LLMeshHeader &mesh = iter->second; + const LLMeshHeader &mesh = iter->second; if (mesh.mPhysicsMeshSize > 0) { return true; @@ -4998,13 +4998,13 @@ bool LLMeshRepoThread::hasPhysicsShapeInHeader(const LLUUID& mesh_id) return false; } -bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id) +bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id) const { LLMutexLock lock(mHeaderMutex); - mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); + mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id); if (iter != mMeshHeader.end() && iter->second.mHeaderSize > 0) { - LLMeshHeader& mesh = iter->second; + const LLMeshHeader& mesh = iter->second; if (mesh.mSkinOffset >= 0 && mesh.mSkinSize > 0) { @@ -5015,10 +5015,10 @@ bool LLMeshRepoThread::hasSkinInfoInHeader(const LLUUID& mesh_id) return false; } -bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id) +bool LLMeshRepoThread::hasHeader(const LLUUID& mesh_id) const { LLMutexLock lock(mHeaderMutex); - mesh_header_map::iterator iter = mMeshHeader.find(mesh_id); + mesh_header_map::const_iterator iter = mMeshHeader.find(mesh_id); return iter != mMeshHeader.end(); } @@ -5033,13 +5033,13 @@ void LLMeshRepository::uploadModel(std::vector& data, LLVector3 mUploadWaitList.push_back(thread); } -S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) +S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod) const { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME; if (mThread && mesh_id.notNull() && LLPrimitive::NO_LOD != lod) { LLMutexLock lock(mThread->mHeaderMutex); - LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id); + LLMeshRepoThread::mesh_header_map::const_iterator iter = mThread->mMeshHeader.find(mesh_id); if (iter != mThread->mMeshHeader.end() && iter->second.mHeaderSize > 0) { const LLMeshHeader& header = iter->second; @@ -5340,7 +5340,7 @@ bool LLMeshCostData::init(const LLMeshHeader& header) } -S32 LLMeshCostData::getSizeByLOD(S32 lod) +S32 LLMeshCostData::getSizeByLOD(S32 lod) const { if (llclamp(lod,0,3) != lod) { @@ -5349,12 +5349,12 @@ S32 LLMeshCostData::getSizeByLOD(S32 lod) return mSizeByLOD[lod]; } -S32 LLMeshCostData::getSizeTotal() +S32 LLMeshCostData::getSizeTotal() const { return mSizeByLOD[0] + mSizeByLOD[1] + mSizeByLOD[2] + mSizeByLOD[3]; } -F32 LLMeshCostData::getEstTrisByLOD(S32 lod) +F32 LLMeshCostData::getEstTrisByLOD(S32 lod) const { if (llclamp(lod,0,3) != lod) { @@ -5363,12 +5363,12 @@ F32 LLMeshCostData::getEstTrisByLOD(S32 lod) return mEstTrisByLOD[lod]; } -F32 LLMeshCostData::getEstTrisMax() +F32 LLMeshCostData::getEstTrisMax() const { return llmax(mEstTrisByLOD[0], mEstTrisByLOD[1], mEstTrisByLOD[2], mEstTrisByLOD[3]); } -F32 LLMeshCostData::getRadiusWeightedTris(F32 radius) +F32 LLMeshCostData::getRadiusWeightedTris(F32 radius) const { F32 max_distance = 512.f; @@ -5412,7 +5412,7 @@ F32 LLMeshCostData::getRadiusWeightedTris(F32 radius) return weighted_avg; } -F32 LLMeshCostData::getEstTrisForStreamingCost() +F32 LLMeshCostData::getEstTrisForStreamingCost() const { LL_DEBUGS("StreamingCost") << "tris_by_lod: " << mEstTrisByLOD[0] << ", " @@ -5439,13 +5439,13 @@ F32 LLMeshCostData::getEstTrisForStreamingCost() return charged_tris; } -F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius) +F32 LLMeshCostData::getRadiusBasedStreamingCost(F32 radius) const { static LLCachedControl mesh_triangle_budget(gSavedSettings, "MeshTriangleBudget"); return getRadiusWeightedTris(radius)/mesh_triangle_budget*15000.f; } -F32 LLMeshCostData::getTriangleBasedStreamingCost() +F32 LLMeshCostData::getTriangleBasedStreamingCost() const { F32 result = ANIMATED_OBJECT_COST_PER_KTRI * 0.001f * getEstTrisForStreamingCost(); return result; -- cgit v1.2.3 From 100861aec17239543b21b3356ed2d8d009ba77ac Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 22 Feb 2025 11:02:45 +0200 Subject: #3596 Fix use of dead references 1. Was posting a would-be-dead reference to a thread 2. loadMeshLODs was getting dead references. It's no longer relevant so just restored logic to wotk like it was before loadMeshLODs --- indra/newview/llmeshrepository.cpp | 83 +++++++------------------------------- 1 file changed, 15 insertions(+), 68 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index bb699031d4..e1fa84b4d8 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1243,55 +1243,6 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) loadMeshLOD(mesh_id, mesh_params, lod); } -void LLMeshRepoThread::loadMeshLODs(const lod_list_t& list) -{ // expectes mMutex to be locked - for (auto lod_pair : list) - { - const LLVolumeParams& mesh_params = lod_pair.first; - const LLUUID& mesh_id = mesh_params.getSculptID(); - S32 lod = lod_pair.second; - - if (hasHeader(mesh_id)) - { //if we have the header, request LOD byte range - - LODRequest req(mesh_params, lod); - { - mLODReqQ.push(req); - LLMeshRepository::sLODProcessing++; - } - } - else - { - LLMutexLock lock(mPendingMutex); - HeaderRequest req(mesh_params); - pending_lod_map::iterator pending = mPendingLOD.find(mesh_id); - - if (pending != mPendingLOD.end()) - { - //append this lod request to existing header request - if (lod < LLModel::NUM_LODS && lod >= 0) - { - pending->second[lod]++; - } - else - { - LL_WARNS(LOG_MESH) << "Invalid LOD request: " << lod << "for mesh" << mesh_id << LL_ENDL; - } - llassert_msg(lod < LLModel::NUM_LODS, "Requested lod is out of bounds"); - } - else - { - //if no header request is pending, fetch header - auto& array = mPendingLOD[mesh_id]; - std::fill(array.begin(), array.end(), 0); - array[lod]++; - - mHeaderReqQ.push(req); - } - } - } -} - void LLMeshRepoThread::loadMeshLOD(const LLUUID& mesh_id, const LLVolumeParams& mesh_params, S32 lod) { if (hasHeader(mesh_id)) @@ -2037,11 +1988,12 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) if (!zero) { //attempt to parse + const LLVolumeParams params(mesh_params); bool posted = mMeshThreadPool->getQueue().post( - [mesh_params, mesh_id, lod, buffer, size] + [params, mesh_id, lod, buffer, size] () { - if (gMeshRepo.mThread->lodReceived(mesh_params, lod, buffer, size) == MESH_OK) + if (gMeshRepo.mThread->lodReceived(params, lod, buffer, size) == MESH_OK) { LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh body for ID " << mesh_id << " - was retrieved from the cache." << LL_ENDL; } @@ -2083,8 +2035,9 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) { LLMutexLock lock(gMeshRepo.mThread->mMutex); - LODRequest req(mesh_params, lod); + LODRequest req(params, lod); gMeshRepo.mThread->mLODReqQ.push(req); + LLMeshRepository::sLODProcessing++; } } delete[] buffer; @@ -3326,7 +3279,7 @@ void LLMeshRepoThread::notifyLoadedMeshes() } else { - gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, mesh.mLOD); + gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, mesh.mLOD, LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail())); } } } @@ -3351,7 +3304,7 @@ void LLMeshRepoThread::notifyLoadedMeshes() // Process the elements free of the lock for (const auto& req : unavil_queue) { - gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); + gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD, req.mLOD); } } else @@ -4620,8 +4573,6 @@ void LLMeshRepository::notifyLoadedMeshes() std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, mPendingRequests.end(), PendingRequestBase::CompareScoreGreater()); } - LLMeshRepoThread::lod_list_t pending_lods; // to avoid locking on each operation, make a list beforehand - pending_lods.reserve(push_count); while (!mPendingRequests.empty() && push_count > 0) { std::unique_ptr& req_p = mPendingRequests.front(); @@ -4630,7 +4581,7 @@ void LLMeshRepository::notifyLoadedMeshes() case MESH_REQUEST_LOD: { PendingRequestLOD* lod = (PendingRequestLOD*)req_p.get(); - pending_lods.emplace_back(lod->mMeshParams, lod->mLOD); + mThread->loadMeshLOD(lod->mMeshParams, lod->mLOD); LLMeshRepository::sLODPending--; break; } @@ -4648,10 +4599,6 @@ void LLMeshRepository::notifyLoadedMeshes() mPendingRequests.erase(mPendingRequests.begin()); push_count--; } - if (!pending_lods.empty()) - { - mThread->loadMeshLODs(pending_lods); - } } //send decomposition requests @@ -4777,16 +4724,16 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol } } -void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod) +void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod) { //called from main thread //get list of objects waiting to be notified this mesh is loaded const auto& mesh_id = mesh_params.getSculptID(); - mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_id); - if (obj_iter != mLoadingMeshes[lod].end()) + mesh_load_map::iterator obj_iter = mLoadingMeshes[request_lod].find(mesh_id); + if (obj_iter != mLoadingMeshes[request_lod].end()) { - F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod); + F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(volume_lod); - LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, lod); + LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, volume_lod); if (sys_volume) { sys_volume->setMeshAssetUnavaliable(true); @@ -4803,12 +4750,12 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, obj_volume->getDetail() == detail && obj_volume->getParams() == mesh_params) { //should force volume to find most appropriate LOD - vobj->setVolume(obj_volume->getParams(), lod); + vobj->setVolume(obj_volume->getParams(), volume_lod); } } } - mLoadingMeshes[lod].erase(obj_iter); + mLoadingMeshes[request_lod].erase(obj_iter); } } -- cgit v1.2.3 From b6bf32e00e25de773b6403b716c655f64b6092c9 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 19 Mar 2025 17:57:50 +0200 Subject: #3488 Prelock mutexes Main thread has priority, it shouldn't be relocking on each loadMeshLOD --- indra/newview/llmeshrepository.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index e1fa84b4d8..a8c6f69425 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -4573,6 +4573,8 @@ void LLMeshRepository::notifyLoadedMeshes() std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, mPendingRequests.end(), PendingRequestBase::CompareScoreGreater()); } + LLMutexTrylock lock3(mThread->mHeaderMutex); + LLMutexTrylock lock4(mThread->mPendingMutex); while (!mPendingRequests.empty() && push_count > 0) { std::unique_ptr& req_p = mPendingRequests.front(); -- cgit v1.2.3