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.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 ++++++++++++++++++++++++++------------ indra/newview/llmeshrepository.h | 9 +++-- 2 files changed, 60 insertions(+), 30 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)); } } } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index c5b2a910ce..449708779b 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -212,9 +212,9 @@ class PendingRequestBase public: struct CompareScoreGreater { - bool operator()(const PendingRequestBase& lhs, const PendingRequestBase& rhs) + bool operator()(const std::unique_ptr& lhs, const std::unique_ptr& rhs) { - return lhs.mScore > rhs.mScore; // greatest = first + return lhs->mScore > rhs->mScore; // greatest = first } }; @@ -229,7 +229,7 @@ public: void setScore(F32 score) { mScore = score; } F32 getScore() const { return mScore; } LLUUID getId() const { return mId; } - virtual EMeshRequestType getRequestType() const { return MESH_REQUEST_UKNOWN; } + virtual EMeshRequestType getRequestType() const = 0; protected: F32 mScore; @@ -758,7 +758,8 @@ public: LLMutex* mMeshMutex; - std::vector mPendingRequests; + typedef std::vector > pending_requests_vec; + pending_requests_vec mPendingRequests; //list of mesh ids awaiting skin info typedef boost::unordered_map > skin_load_map; -- cgit v1.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 ++++++++++++++++++++++---- indra/newview/llmeshrepository.h | 7 +++++-- 2 files changed, 27 insertions(+), 6 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; diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 449708779b..7c0676255e 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -328,8 +328,9 @@ class LLMeshRepoThread : public LLThread { public: - volatile static S32 sActiveHeaderRequests; - volatile static S32 sActiveLODRequests; + static std::atomic sActiveHeaderRequests; + static std::atomic sActiveLODRequests; + static std::atomic sActiveSkinRequests; static U32 sMaxConcurrentRequests; static S32 sRequestLowWater; static S32 sRequestHighWater; @@ -499,6 +500,8 @@ public: static void decActiveLODRequests(); static void incActiveHeaderRequests(); static void decActiveHeaderRequests(); + static void incActiveSkinRequests(); + static void decActiveSkinRequests(); // Set the caps strings and preferred version for constructing // mesh fetch URLs. -- cgit v1.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 ++++++++++++++++++++++++------------- indra/newview/llmeshrepository.h | 3 + 2 files changed, 95 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 diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 7c0676255e..8ced9a7ef6 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -466,6 +466,9 @@ public: void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); + typedef std::vector > lod_list_t; + void loadMeshLODs(const lod_list_t& mesh_vect); + bool fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry = true); bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry = true); EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); -- cgit v1.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 ++++++++++++++++++++----------------- indra/newview/llmeshrepository.h | 5 +- 2 files changed, 59 insertions(+), 46 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); } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 8ced9a7ef6..703f4cf9ce 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -433,7 +433,7 @@ public: std::deque mLoadedQ; //map of pending header requests and currently desired LODs - typedef std::unordered_map > pending_lod_map; + typedef std::unordered_map > pending_lod_map; pending_lod_map mPendingLOD; // map of mesh ID to skin info (mirrors LLMeshRepository::mSkinMap) @@ -525,6 +525,9 @@ private: LLCore::HttpHandle getByteRange(const std::string & url, size_t offset, size_t len, const LLCore::HttpHandler::ptr_t &handler); + + // Mutex: mMutex must be alerady locked when calling + void loadMeshLOD(const LLUUID &mesh_id, const LLVolumeParams& mesh_params, S32 lod); }; -- cgit v1.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 +++++++++++++++++++++++++++++--------- indra/newview/llmeshrepository.h | 4 +-- 2 files changed, 57 insertions(+), 18 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) diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 703f4cf9ce..e000d2aef9 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -318,8 +318,8 @@ public: S32 mPhysicsMeshOffset = -1; S32 mPhysicsMeshSize = -1; - S32 mLodOffset[4] = { -1 }; - S32 mLodSize[4] = { -1 }; + S32 mLodOffset[LLModel::NUM_LODS] = { -1 }; + S32 mLodSize[LLModel::NUM_LODS] = { -1 }; bool m404 = false; }; -- cgit v1.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.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 ++++++++++++++++++++------------------ indra/newview/llmeshrepository.h | 3 +- 2 files changed, 32 insertions(+), 28 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 diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index e000d2aef9..f30e6cce12 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -320,6 +320,7 @@ public: S32 mLodOffset[LLModel::NUM_LODS] = { -1 }; S32 mLodSize[LLModel::NUM_LODS] = { -1 }; + S32 mHeaderSize = -1; bool m404 = false; }; @@ -341,7 +342,7 @@ public: LLCondition* mSignal; //map of known mesh headers - typedef boost::unordered_map> mesh_header_map; // pair is header_size and data + typedef boost::unordered_map mesh_header_map; // pair is header_size and data mesh_header_map mMeshHeader; class HeaderRequest : public RequestStats -- cgit v1.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/llappviewer.cpp | 2 +- indra/newview/llmeshrepository.cpp | 219 ++++++++++++++++++++++++++++++++----- indra/newview/llmeshrepository.h | 49 ++++++++- 3 files changed, 242 insertions(+), 28 deletions(-) (limited to 'indra/newview/llmeshrepository.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c770b7c917..00624ebc6a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4145,7 +4145,7 @@ U32 LLAppViewer::getTextureCacheVersion() U32 LLAppViewer::getDiskCacheVersion() { // Viewer disk cache version intorduced in Simple Cache Viewer, change if the cache format changes. - const U32 DISK_CACHE_VERSION = 1; + const U32 DISK_CACHE_VERSION = 2; return DISK_CACHE_VERSION ; } 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); } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index f30e6cce12..d11ea581cd 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -307,19 +307,66 @@ public: m404 = header.has("404"); } +private: + + enum EDiskCacheFlags { + FLAG_SKIN = 1 << LLModel::NUM_LODS, + FLAG_PHYSCONVEX = 1 << (LLModel::NUM_LODS + 1), + FLAG_PHYSMESH = 1 << (LLModel::NUM_LODS + 2), + }; +public: + U32 getFlags() + { + U32 flags = 0; + for (U32 i = 0; i < LLModel::NUM_LODS; i++) + { + if (mLodInCache[i]) + { + flags |= 1 << i; + } + } + if (mSkinInCache) + { + flags |= FLAG_SKIN; + } + if (mPhysicsConvexInCache) + { + flags |= FLAG_PHYSCONVEX; + } + if (mPhysicsMeshInCache) + { + flags |= FLAG_PHYSMESH; + } + return flags; + } + + void setFromFlags(U32 flags) + { + for (U32 i = 0; i < LLModel::NUM_LODS; i++) + { + mLodInCache[i] = (flags & (1 << i)) != 0; + } + mSkinInCache = (flags & FLAG_SKIN) != 0; + mPhysicsConvexInCache = (flags & FLAG_PHYSCONVEX) != 0; + mPhysicsMeshInCache = (flags & FLAG_PHYSMESH) != 0; + } S32 mVersion = -1; S32 mSkinOffset = -1; S32 mSkinSize = -1; + bool mSkinInCache = false; S32 mPhysicsConvexOffset = -1; S32 mPhysicsConvexSize = -1; + bool mPhysicsConvexInCache = false; S32 mPhysicsMeshOffset = -1; S32 mPhysicsMeshSize = -1; + bool mPhysicsMeshInCache = false; S32 mLodOffset[LLModel::NUM_LODS] = { -1 }; S32 mLodSize[LLModel::NUM_LODS] = { -1 }; + bool mLodInCache[LLModel::NUM_LODS] = { false }; S32 mHeaderSize = -1; bool m404 = false; @@ -472,7 +519,7 @@ public: bool fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry = true); bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry = true); - EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); + EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size, U32 flags = 0); EMeshProcessingResult lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size); bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size); bool decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size); -- cgit v1.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 +++++++++++++++++--------------------- indra/newview/llmeshrepository.h | 5 ++ 2 files changed, 48 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 diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index d11ea581cd..2e29daf9ec 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -576,6 +576,11 @@ private: // Mutex: mMutex must be alerady locked when calling void loadMeshLOD(const LLUUID &mesh_id, const LLVolumeParams& mesh_params, S32 lod); + + // Threads: Repo thread only + U8* getDiskCacheBuffer(S32 size); + S32 mDiskCacheBufferSize = 0; + U8* mDiskCacheBuffer = nullptr; }; -- cgit v1.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 ++++++++++-------------------- indra/newview/llmeshrepository.h | 6 +++--- 2 files changed, 13 insertions(+), 23 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 diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 2e29daf9ec..294d01f868 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -517,8 +517,8 @@ public: typedef std::vector > lod_list_t; void loadMeshLODs(const lod_list_t& mesh_vect); - bool fetchMeshHeader(const LLVolumeParams& mesh_params, bool can_retry = true); - bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, bool can_retry = true); + bool fetchMeshHeader(const LLVolumeParams& mesh_params); + bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod); EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size, U32 flags = 0); EMeshProcessingResult lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size); bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size); @@ -537,7 +537,7 @@ public: //send request for skin info, returns true if header info exists // (should hold onto mesh_id and try again later if header info does not exist) - bool fetchMeshSkinInfo(const LLUUID& mesh_id, bool can_retry = true); + bool fetchMeshSkinInfo(const LLUUID& mesh_id); //send request for decomposition, returns true if header info exists // (should hold onto mesh_id and try again later if header info does not exist) -- cgit v1.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 +++++++++++++++++++++++--------------- indra/newview/llmeshrepository.h | 4 +- 2 files changed, 60 insertions(+), 39 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); } } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 294d01f868..1288b6045f 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -386,6 +386,8 @@ public: LLMutex* mMutex; LLMutex* mHeaderMutex; + LLMutex* mLoadedMutex; + LLMutex* mPendingMutex; LLCondition* mSignal; //map of known mesh headers @@ -574,7 +576,7 @@ private: size_t offset, size_t len, const LLCore::HttpHandler::ptr_t &handler); - // Mutex: mMutex must be alerady locked when calling + // Mutex: acquires mPendingMutex, mMutex and mHeaderMutex as needed void loadMeshLOD(const LLUUID &mesh_id, const LLVolumeParams& mesh_params, S32 lod); // Threads: Repo thread only -- cgit v1.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 ++++++++++++++++++++++++++++--------- indra/newview/llmeshrepository.h | 6 +- indra/newview/lltextureview.cpp | 2 +- 3 files changed, 169 insertions(+), 56 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()) { diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 1288b6045f..44f74e32a2 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -493,6 +493,8 @@ public: // workqueue for processing generic requests LL::WorkQueue mWorkQueue; + // lods have their own thread due to costly cacheOptimize() calls + std::unique_ptr mLodThreadPool; // llcorehttp library interface objects. LLCore::HttpStatus mHttpStatus; @@ -747,12 +749,12 @@ public: static U32 sLODPending; static U32 sLODProcessing; static U32 sCacheBytesRead; - static U32 sCacheBytesWritten; + static std::atomic sCacheBytesWritten; static U32 sCacheBytesHeaders; static U32 sCacheBytesSkins; static U32 sCacheBytesDecomps; static U32 sCacheReads; - static U32 sCacheWrites; + static std::atomic sCacheWrites; static U32 sMaxLockHoldoffs; // Maximum sequential locking failures static LLDeadmanTimer sQuiescentTimer; // Time-to-complete-mesh-downloads after significant events diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index bda53f66eb..78d930c05c 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -642,7 +642,7 @@ void LLGLTexMemBar::draw() text = llformat("Mesh: Reqs(Tot/Htp/Big): %u/%u/%u Rtr/Err: %u/%u Cread/Cwrite: %u/%u Low/At/High: %d/%d/%d", LLMeshRepository::sMeshRequestCount, LLMeshRepository::sHTTPRequestCount, LLMeshRepository::sHTTPLargeRequestCount, LLMeshRepository::sHTTPRetryCount, LLMeshRepository::sHTTPErrorCount, - LLMeshRepository::sCacheReads, LLMeshRepository::sCacheWrites, + (U32)LLMeshRepository::sCacheReads, (U32)LLMeshRepository::sCacheWrites, LLMeshRepoThread::sRequestLowWater, LLMeshRepoThread::sRequestWaterLevel, LLMeshRepoThread::sRequestHighWater); LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*2, text_color, LLFontGL::LEFT, LLFontGL::TOP); -- cgit v1.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.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 ++++++++++++++++++++++----------- indra/newview/llmeshrepository.h | 1 + 2 files changed, 23 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); }); } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 44f74e32a2..3a8b4fb5c5 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -388,6 +388,7 @@ public: LLMutex* mHeaderMutex; LLMutex* mLoadedMutex; LLMutex* mPendingMutex; + LLMutex* mSkinMapMutex; LLCondition* mSignal; //map of known mesh headers -- cgit v1.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.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.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 ++++++++++++++++++++++++++++++++----- indra/newview/llmeshrepository.h | 2 +- 2 files changed, 121 insertions(+), 19 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()) { diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 3a8b4fb5c5..15bc7575a9 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -495,7 +495,7 @@ public: // workqueue for processing generic requests LL::WorkQueue mWorkQueue; // lods have their own thread due to costly cacheOptimize() calls - std::unique_ptr mLodThreadPool; + std::unique_ptr mMeshThreadPool; // llcorehttp library interface objects. LLCore::HttpStatus mHttpStatus; -- cgit v1.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 +++++- indra/newview/llviewermessage.cpp | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) (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()) { diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 0c702b24c1..db91678254 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3147,8 +3147,9 @@ void send_agent_update(bool force_send, bool send_reliable) LL_PROFILE_ZONE_SCOPED; llassert(!gCubeSnapshot); - if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE - && gAgent.getTeleportState() != LLAgent::TELEPORT_ARRIVING) + LLAgent::ETeleportState tp_state = gAgent.getTeleportState(); + if (tp_state != LLAgent::TELEPORT_NONE + && tp_state != LLAgent::TELEPORT_ARRIVING) { // We don't care if they want to send an agent update, they're not allowed // until the target simulator is ready to receive them @@ -3323,7 +3324,36 @@ void send_agent_update(bool force_send, bool send_reliable) msg->addVector3Fast(_PREHASH_CameraAtAxis, camera_at); msg->addVector3Fast(_PREHASH_CameraLeftAxis, LLViewerCamera::getInstance()->getLeftAxis()); msg->addVector3Fast(_PREHASH_CameraUpAxis, LLViewerCamera::getInstance()->getUpAxis()); - msg->addF32Fast(_PREHASH_Far, gAgentCamera.mDrawDistance); + + static F32 last_draw_disatance_step = 1024; + if (tp_state == LLAgent::TELEPORT_ARRIVING || LLStartUp::getStartupState() < STATE_MISC) + { + // Inform interest list, prioritize closer area. + // Reason: currently server doesn't distance sort attachments, by restricting range + // we reduce the number of attachments sent to the viewer, thus prioritizing + // closer ones. + // Todo: revise and remove once server gets distance sorting. + last_draw_disatance_step = llmax((F32)(gAgentCamera.mDrawDistance / 2.f), 64.f); + msg->addF32Fast(_PREHASH_Far, last_draw_disatance_step); + } + else if (last_draw_disatance_step < gAgentCamera.mDrawDistance) + { + static LLFrameTimer last_step_time; + if (last_step_time.getElapsedTimeF32() > 1.f) + { + // gradually increase draw distance + // Idealy this should be not per second, but based on how loaded + // mesh thread is, but hopefully this is temporary. + last_step_time.reset(); + F32 step = gAgentCamera.mDrawDistance * 0.1f; + last_draw_disatance_step = llmin(last_draw_disatance_step + step, gAgentCamera.mDrawDistance); + } + msg->addF32Fast(_PREHASH_Far, last_draw_disatance_step); + } + else + { + msg->addF32Fast(_PREHASH_Far, gAgentCamera.mDrawDistance); + } msg->addU32Fast(_PREHASH_ControlFlags, control_flags); -- cgit v1.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 ++++++++++++++++++++++---------------- indra/newview/llviewermessage.cpp | 2 +- indra/newview/llvoavatar.cpp | 14 ++++++++++++- indra/newview/llvoavatar.h | 1 + 4 files changed, 38 insertions(+), 19 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? diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index db91678254..5fd820f91d 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3333,7 +3333,7 @@ void send_agent_update(bool force_send, bool send_reliable) // we reduce the number of attachments sent to the viewer, thus prioritizing // closer ones. // Todo: revise and remove once server gets distance sorting. - last_draw_disatance_step = llmax((F32)(gAgentCamera.mDrawDistance / 2.f), 64.f); + last_draw_disatance_step = llmax((F32)(gAgentCamera.mDrawDistance / 2.f), 50.f); msg->addF32Fast(_PREHASH_Far, last_draw_disatance_step); } else if (last_draw_disatance_step < gAgentCamera.mDrawDistance) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 1ab27d752d..54bbc4c88a 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -3226,6 +3226,7 @@ void LLVOAvatar::idleUpdateLoadingEffect() if (mFirstFullyVisible) { mFirstFullyVisible = false; + mLastCloudAttachmentCount = (S32)mSimAttachments.size(); mFirstDecloudTime = mFirstAppearanceMessageTimer.getElapsedTimeF32(); if (isSelf()) { @@ -8398,7 +8399,7 @@ bool LLVOAvatar::updateIsFullyLoaded() ); // compare amount of attachments to one reported by simulator - if (!loading && !isSelf() && rez_status < 4 && mLastCloudAttachmentCount < mSimAttachments.size()) + if (!isSelf() && mLastCloudAttachmentCount < mSimAttachments.size() && mSimAttachments.size() > 0) { S32 attachment_count = getAttachmentCount(); if (mLastCloudAttachmentCount != attachment_count) @@ -8416,6 +8417,11 @@ bool LLVOAvatar::updateIsFullyLoaded() // waiting loading = true; } + else if (!loading) + { + // for hasFirstFullAttachmentData + mLastCloudAttachmentCount = (S32)mSimAttachments.size(); + } } } updateRezzedStatusTimers(rez_status); @@ -8529,6 +8535,12 @@ bool LLVOAvatar::isFullyLoaded() const return (mRenderUnloadedAvatar || mFullyLoaded); } +bool LLVOAvatar::hasFirstFullAttachmentData() const +{ + return !mFirstFullyVisible // Avatar is fully visible, have all data + || mLastCloudAttachmentCount >= (S32)mSimAttachments.size(); +} + bool LLVOAvatar::isTooComplex() const { bool too_complex; diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index dd1725c322..263c3dadf6 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -385,6 +385,7 @@ public: //-------------------------------------------------------------------- public: bool isFullyLoaded() const; + bool hasFirstFullAttachmentData() const; F32 getFirstDecloudTime() const {return mFirstDecloudTime;} // check and return current state relative to limits -- cgit v1.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 +++++++++++++++++------------------ indra/newview/llmeshrepository.h | 8 ++++---- 2 files changed, 21 insertions(+), 22 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(); } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 15bc7575a9..07d3e465d1 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -776,10 +776,10 @@ public: void unregisterMesh(LLVOVolume* volume); //mesh management functions - S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1); + S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 new_lod = 0, S32 last_lod = -1); void notifyLoadedMeshes(); - void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume); + void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod); void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod); void notifySkinInfoReceived(LLMeshSkinInfo* info); void notifySkinInfoUnavailable(const LLUUID& info); @@ -814,7 +814,7 @@ public: static void metricsProgress(unsigned int count); static void metricsUpdate(); - typedef boost::unordered_map > mesh_load_map; + typedef std::unordered_map > mesh_load_map; mesh_load_map mLoadingMeshes[4]; typedef std::unordered_map> skin_map; @@ -829,7 +829,7 @@ public: pending_requests_vec mPendingRequests; //list of mesh ids awaiting skin info - typedef boost::unordered_map > skin_load_map; + typedef std::unordered_map > skin_load_map; skin_load_map mLoadingSkins; //list of mesh ids awaiting decompositions -- cgit v1.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 +++++++++++++++++++++++++++++++------- indra/newview/llmeshrepository.h | 1 + 2 files changed, 42 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); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 07d3e465d1..ed4514dff8 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -520,6 +520,7 @@ public: void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); typedef std::vector > lod_list_t; + // Mutex: must be holding mMutex when called void loadMeshLODs(const lod_list_t& mesh_vect); bool fetchMeshHeader(const LLVolumeParams& mesh_params); -- cgit v1.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 +++++++++++++++++++------------------- indra/newview/llmeshrepository.h | 26 +++++++++++++------------- 2 files changed, 32 insertions(+), 32 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; diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index ed4514dff8..95a596734c 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -530,9 +530,9 @@ public: bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size); bool decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size); EMeshProcessingResult physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size); - bool hasPhysicsShapeInHeader(const LLUUID& mesh_id); - bool hasSkinInfoInHeader(const LLUUID& mesh_id); - bool hasHeader(const LLUUID& mesh_id); + bool hasPhysicsShapeInHeader(const LLUUID& mesh_id) const; + bool hasSkinInfoInHeader(const LLUUID& mesh_id) const; + bool hasHeader(const LLUUID& mesh_id) const; void notifyLoadedMeshes(); S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); @@ -699,35 +699,35 @@ public: bool init(const LLMeshHeader& header); // Size for given LOD - S32 getSizeByLOD(S32 lod); + S32 getSizeByLOD(S32 lod) const; // Sum of all LOD sizes. - S32 getSizeTotal(); + S32 getSizeTotal() const; // Estimated triangle counts for the given LOD. - F32 getEstTrisByLOD(S32 lod); + F32 getEstTrisByLOD(S32 lod) const; // Estimated triangle counts for the largest LOD. Typically this // is also the "high" LOD, but not necessarily. - F32 getEstTrisMax(); + F32 getEstTrisMax() const; // Triangle count as computed by original streaming cost // formula. Triangles in each LOD are weighted based on how // frequently they will be seen. // This was called "unscaled_value" in the original getStreamingCost() functions. - F32 getRadiusWeightedTris(F32 radius); + F32 getRadiusWeightedTris(F32 radius) const; // Triangle count used by triangle-based cost formula. Based on // triangles in highest LOD plus potentially partial charges for // lower LODs depending on complexity. - F32 getEstTrisForStreamingCost(); + F32 getEstTrisForStreamingCost() const; // Streaming cost. This should match the server-side calculation // for the corresponding volume. - F32 getRadiusBasedStreamingCost(F32 radius); + F32 getRadiusBasedStreamingCost(F32 radius) const; // New streaming cost formula, currently only used for animated objects. - F32 getTriangleBasedStreamingCost(); + F32 getTriangleBasedStreamingCost() const; private: // From the "size" field of the mesh header. LOD 0=lowest, 3=highest. @@ -793,7 +793,7 @@ public: void fetchPhysicsShape(const LLUUID& mesh_id); bool hasPhysicsShape(const LLUUID& mesh_id); bool hasSkinInfo(const LLUUID& mesh_id); - bool hasHeader(const LLUUID& mesh_id); + bool hasHeader(const LLUUID& mesh_id) const; void buildHull(const LLVolumeParams& params, S32 detail); void buildPhysicsMesh(LLModel::Decomposition& decomp); @@ -807,7 +807,7 @@ public: LLHandle fee_observer= (LLHandle()), LLHandle upload_observer = (LLHandle())); - S32 getMeshSize(const LLUUID& mesh_id, S32 lod); + S32 getMeshSize(const LLUUID& mesh_id, S32 lod) const; // Quiescent timer management, main thread only. static void metricsStart(); -- cgit v1.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 +++++++------------------------------- indra/newview/llmeshrepository.h | 6 +-- 2 files changed, 16 insertions(+), 73 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); } } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 95a596734c..0d9da32e27 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -519,10 +519,6 @@ public: void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); - typedef std::vector > lod_list_t; - // Mutex: must be holding mMutex when called - void loadMeshLODs(const lod_list_t& mesh_vect); - bool fetchMeshHeader(const LLVolumeParams& mesh_params); bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod); EMeshProcessingResult headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size, U32 flags = 0); @@ -781,7 +777,7 @@ public: void notifyLoadedMeshes(); void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod); - void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod); + void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod); void notifySkinInfoReceived(LLMeshSkinInfo* info); void notifySkinInfoUnavailable(const LLUUID& info); void notifyDecompositionReceived(LLModel::Decomposition* info); -- cgit v1.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.3