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 ++++++++++++++++++++++--------------- indra/newview/llmeshrepository.h | 90 ++++++++++++++++++---- 2 files changed, 165 insertions(+), 74 deletions(-) (limited to 'indra/newview') 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)); } } } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index b5c2424578..c5b2a910ce 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -63,6 +63,16 @@ typedef enum e_mesh_processing_result_enum MESH_UNKNOWN } EMeshProcessingResult; +typedef enum e_mesh_request_type_enum +{ + MESH_REQUEST_HEADER, + MESH_REQUEST_LOD, + MESH_REQUEST_SKIN, + MESH_REQUEST_DECOMPOSITION, + MESH_REQUEST_PHYSICS, + MESH_REQUEST_UKNOWN +} EMeshRequestType; + class LLMeshUploadData { public: @@ -183,7 +193,8 @@ public: class RequestStats { public: - RequestStats() : mRetries(0) {}; + + RequestStats() :mRetries(0) {}; void updateTime(); bool canRetry() const; @@ -195,6 +206,67 @@ private: LLFrameTimer mTimer; }; + +class PendingRequestBase +{ +public: + struct CompareScoreGreater + { + bool operator()(const PendingRequestBase& lhs, const PendingRequestBase& rhs) + { + return lhs.mScore > rhs.mScore; // greatest = first + } + }; + + PendingRequestBase() : mScore(0.f) {}; + virtual ~PendingRequestBase() {} + + bool operator<(const PendingRequestBase& rhs) const + { + return mId < rhs.mId; + } + + void setScore(F32 score) { mScore = score; } + F32 getScore() const { return mScore; } + LLUUID getId() const { return mId; } + virtual EMeshRequestType getRequestType() const { return MESH_REQUEST_UKNOWN; } + +protected: + F32 mScore; + LLUUID mId; +}; + +class PendingRequestLOD : public PendingRequestBase +{ +public: + LLVolumeParams mMeshParams; + S32 mLOD; + + PendingRequestLOD(const LLVolumeParams& mesh_params, S32 lod) + : PendingRequestBase(), mMeshParams(mesh_params), mLOD(lod) + { + mId = mMeshParams.getSculptID(); + } + + EMeshRequestType getRequestType() const override { return MESH_REQUEST_LOD; } +}; + +class PendingRequestUUID : public PendingRequestBase +{ +public: + + PendingRequestUUID(const LLUUID& id, EMeshRequestType type) + : PendingRequestBase(), mRequestType(type) + { + mId = id; + } + + EMeshRequestType getRequestType() const override { return mRequestType; } + +private: + EMeshRequestType mRequestType; +}; + class LLMeshHeader { public: @@ -292,22 +364,13 @@ public: public: LLVolumeParams mMeshParams; S32 mLOD; - F32 mScore; LODRequest(const LLVolumeParams& mesh_params, S32 lod) - : RequestStats(), mMeshParams(mesh_params), mLOD(lod), mScore(0.f) + : RequestStats(), mMeshParams(mesh_params), mLOD(lod) { } }; - struct CompareScoreGreater - { - bool operator()(const LODRequest& lhs, const LODRequest& rhs) - { - return lhs.mScore > rhs.mScore; // greatest = first - } - }; - class UUIDBasedRequest : public RequestStats { public: @@ -695,15 +758,12 @@ public: LLMutex* mMeshMutex; - std::vector mPendingRequests; + std::vector mPendingRequests; //list of mesh ids awaiting skin info typedef boost::unordered_map > skin_load_map; skin_load_map mLoadingSkins; - //list of mesh ids that need to send skin info fetch requests - std::queue mPendingSkinRequests; - //list of mesh ids awaiting decompositions std::unordered_set mLoadingDecompositions; -- cgit v1.2.3 From 3271408650f080281609c2704c80b6c31c62b3a5 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 11 Nov 2024 22:48:18 +0200 Subject: #1186 Make mesh repository account for avatars when calculating priority --- indra/newview/llmeshrepository.cpp | 81 ++++++++++++++++++++++++++------------ indra/newview/llmeshrepository.h | 9 +++-- 2 files changed, 60 insertions(+), 30 deletions(-) (limited to 'indra/newview') 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.2.3 From 8e17f0c094e12faea9b9268330b24c77913fe9f3 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 18 Nov 2024 20:35:33 +0200 Subject: #1186 Fix skin request not being counted for the total --- indra/newview/llmeshrepository.cpp | 26 ++++++++++++++++++++++---- indra/newview/llmeshrepository.h | 7 +++++-- 2 files changed, 27 insertions(+), 6 deletions(-) (limited to 'indra/newview') 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.2.3 From e20f4c0e4f10151bb5e5f208ea49b6065dd667ca Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 19 Nov 2024 19:47:23 +0200 Subject: #1186 Fix mesh queues getting overfilled Account for queue size instead of just active request Reduce mutex locking Prioritize skininfo queue over lod queue to lessen issues with t-poses --- indra/newview/llmeshrepository.cpp | 139 ++++++++++++++++++++++++------------- indra/newview/llmeshrepository.h | 3 + 2 files changed, 95 insertions(+), 47 deletions(-) (limited to 'indra/newview') 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.2.3 From a428b7dfbd7d5b766b2ae6e4fbd2c8c510ee19a1 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 5 Dec 2024 01:32:54 +0200 Subject: #1186 Improve handling of duplciate requests --- indra/newview/llmeshrepository.cpp | 100 ++++++++++++++++++++----------------- indra/newview/llmeshrepository.h | 5 +- 2 files changed, 59 insertions(+), 46 deletions(-) (limited to 'indra/newview') 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.2.3 From 142cf21cc111c7d75f4d75bab2b35364fbaf3624 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 7 Nov 2024 16:35:13 +0200 Subject: viewer#3037 hasPendingAttachedMeshes wasn't checking some objects --- indra/newview/llvoavatar.cpp | 116 ++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 50 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 7bc9d06f9a..1ab27d752d 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -682,7 +682,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mFullyLoaded(false), mPreviousFullyLoaded(false), mFullyLoadedInitialized(false), - mLastCloudAttachmentCount(0), + mLastCloudAttachmentCount(-1), mVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN), mLoadedCallbacksPaused(false), mLoadedCallbackTextures(0), @@ -7710,6 +7710,64 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) } } +bool check_object_for_mesh_loading(LLViewerObject* objectp) +{ + if (!objectp || !objectp->getVolume()) + { + return false; + } + LLVolume* volp = objectp->getVolume(); + const LLUUID& mesh_id = volp->getParams().getSculptID(); + if (mesh_id.isNull()) + { + // No mesh nor skin info needed + return false; + } + + if (volp->isMeshAssetUnavaliable()) + { + // Mesh failed to load, do not expect it + return false; + } + + if (!objectp->mDrawable) + { + return false; + } + + LLVOVolume* pvobj = objectp->mDrawable->getVOVolume(); + if (pvobj) + { + if (!pvobj->isMesh()) + { + // Not a mesh + return false; + } + + if (!volp->isMeshAssetLoaded()) + { + // Waiting for mesh + return true; + } + + const LLMeshSkinInfo* skin_data = pvobj->getSkinInfo(); + if (skin_data) + { + // Skin info present, done + return false; + } + + if (pvobj->isSkinInfoUnavaliable()) + { + // Load failed or info not present, don't expect it + return false; + } + } + + // object is not ready + return true; +} + bool LLVOAvatar::hasPendingAttachedMeshes() { for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); @@ -7724,62 +7782,20 @@ bool LLVOAvatar::hasPendingAttachedMeshes() ++attachment_iter) { LLViewerObject* objectp = attachment_iter->get(); - if (objectp) + if (objectp && !objectp->isDead()) { + if (check_object_for_mesh_loading(objectp)) + { + return true; + } LLViewerObject::const_child_list_t& child_list = objectp->getChildren(); for (LLViewerObject::child_list_t::const_iterator iter1 = child_list.begin(); iter1 != child_list.end(); ++iter1) { LLViewerObject* objectchild = *iter1; - if (objectchild && objectchild->getVolume()) + if (check_object_for_mesh_loading(objectchild)) { - const LLUUID& mesh_id = objectchild->getVolume()->getParams().getSculptID(); - if (mesh_id.isNull()) - { - // No mesh nor skin info needed - continue; - } - - if (objectchild->getVolume()->isMeshAssetUnavaliable()) - { - // Mesh failed to load, do not expect it - continue; - } - - if (objectchild->mDrawable) - { - LLVOVolume* pvobj = objectchild->mDrawable->getVOVolume(); - if (pvobj) - { - if (!pvobj->isMesh()) - { - // Not a mesh - continue; - } - - if (!objectchild->getVolume()->isMeshAssetLoaded()) - { - // Waiting for mesh - return true; - } - - const LLMeshSkinInfo* skin_data = pvobj->getSkinInfo(); - if (skin_data) - { - // Skin info present, done - continue; - } - - if (pvobj->isSkinInfoUnavaliable()) - { - // Load failed or info not present, don't expect it - continue; - } - } - - // objectchild is not ready - return true; - } + return true; } } } -- cgit v1.2.3 From 1a3b221ea4628f2161332c2b6d0163a49bb8ab20 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 28 Jan 2025 17:10:47 +0200 Subject: #1186 Speed up mesh header post processing 1. Don't do an extra skin request unless there is info about a skin 2. Proccess lods on the go if they were in initial packet --- indra/newview/llmeshrepository.cpp | 71 +++++++++++++++++++++++++++++--------- indra/newview/llmeshrepository.h | 4 +-- 2 files changed, 57 insertions(+), 18 deletions(-) (limited to 'indra/newview') 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.2.3 From 8456572db0d576d3a90d2c5d8a5a41ff298af435 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 28 Jan 2025 20:40:41 +0200 Subject: #1186 Profiling --- indra/newview/llmeshrepository.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index b5784580f5..8381d3e2e2 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -911,6 +911,7 @@ void LLMeshRepoThread::run() // On the other hand, this may actually be an effective and efficient scheme... mSignal->wait(); + LL_PROFILE_ZONE_NAMED("mesh_thread_loop") if (LLApp::isExiting()) { @@ -1944,6 +1945,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; const LLUUID mesh_id = mesh_params.getSculptID(); LLSD header_data; @@ -2089,6 +2091,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; if (data == NULL || data_size == 0) { return MESH_NO_DATA; @@ -2131,6 +2134,7 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; LLSD skin; if (data_size > 0) @@ -2180,6 +2184,7 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; LLSD decomp; if (data_size > 0) @@ -2216,6 +2221,7 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3 EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; LLSD physics_shape; LLModel::Decomposition* d = new LLModel::Decomposition(); @@ -3047,6 +3053,8 @@ void LLMeshRepoThread::notifyLoadedMeshes() return; } + LL_PROFILE_ZONE_SCOPED; + if (!mLoadedQ.empty()) { std::deque loaded_queue; @@ -3367,6 +3375,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* body_offset */, U8 * data, S32 data_size) { + LL_PROFILE_ZONE_SCOPED; LLUUID mesh_id = mMeshParams.getSculptID(); bool success = (!MESH_HEADER_PROCESS_FAILED) && ((data != NULL) == (data_size > 0)); // if we have data but no size or have size but no data, something is wrong; -- cgit v1.2.3 From 52fb53a4d6e0856c96044c654970dcbb1cc1f235 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 29 Jan 2025 23:05:35 +0200 Subject: #3488 Make mesh_header_map a bit simpler preparation for further work --- indra/newview/llmeshrepository.cpp | 57 ++++++++++++++++++++------------------ indra/newview/llmeshrepository.h | 3 +- 2 files changed, 32 insertions(+), 28 deletions(-) (limited to 'indra/newview') 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.2.3 From c405cd379a736722740ac241d36bcffb88c236c2 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 30 Jan 2025 00:50:49 +0200 Subject: #3488 Avoid reading meshes from disk when not nessesary LLMeshHeaderHandler preallocates file with empty data, there is no point reading large empty sections so write presense flags at the start of the file --- indra/newview/llappviewer.cpp | 2 +- indra/newview/llmeshrepository.cpp | 219 ++++++++++++++++++++++++++++++++----- indra/newview/llmeshrepository.h | 49 ++++++++- 3 files changed, 242 insertions(+), 28 deletions(-) (limited to 'indra/newview') 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.2.3 From e97976831d883d9c8ff40a0442a5905ac05dd45a Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Thu, 30 Jan 2025 23:02:53 +0200 Subject: #3488 Minimize allocations --- indra/newview/llmeshrepository.cpp | 98 +++++++++++++++++--------------------- indra/newview/llmeshrepository.h | 5 ++ 2 files changed, 48 insertions(+), 55 deletions(-) (limited to 'indra/newview') 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.2.3 From 314ef5389cb57646fc20971335e65b6a8dab7235 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 31 Jan 2025 19:09:05 +0200 Subject: #3488 Fix last retry not saving handle Request was sent and handle is valid, save it --- indra/newview/llmeshrepository.cpp | 30 ++++++++++-------------------- indra/newview/llmeshrepository.h | 6 +++--- 2 files changed, 13 insertions(+), 23 deletions(-) (limited to 'indra/newview') 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.2.3 From 33303b9011c8be0dae46a3005fa2e89ee5d64ef9 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 31 Jan 2025 21:44:57 +0200 Subject: #3488 Split mesh thread mutexes Tracy shows that thread waits a lot on mutexes. Split in and out mutexes, so that new work can be scheduled by main thread without blocking handling --- indra/newview/llmeshrepository.cpp | 95 +++++++++++++++++++++++--------------- indra/newview/llmeshrepository.h | 4 +- 2 files changed, 60 insertions(+), 39 deletions(-) (limited to 'indra/newview') 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.2.3 From 7eb9dd3e0d744406db4006f42ee217a67ab3c6e0 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 01:33:29 +0200 Subject: #3488 Move lod processing out of mesh thread --- indra/newview/llmeshrepository.cpp | 217 ++++++++++++++++++++++++++++--------- indra/newview/llmeshrepository.h | 6 +- indra/newview/lltextureview.cpp | 2 +- 3 files changed, 169 insertions(+), 56 deletions(-) (limited to 'indra/newview') 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.2.3 From f5f98e1c285f826f0a20af6188f2fcd1d7c1fe6e Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 02:29:10 +0200 Subject: #3488 Fix mesh header condition --- indra/newview/llmeshrepository.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 347e7fde70..6f95544503 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1820,8 +1820,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) } U32 flags = 0; memcpy(&flags, buffer + 2 * sizeof(U32), sizeof(U32)); - // Todo: parse and pass flags, they are the reason for the preamble - if (headerReceived(mesh_params, buffer + CACHE_PREAMBLE_SIZE, bytes, flags) == MESH_OK) + if (headerReceived(mesh_params, buffer + CACHE_PREAMBLE_SIZE, bytes - CACHE_PREAMBLE_SIZE, flags) == MESH_OK) { LL_DEBUGS(LOG_MESH) << "Mesh/Cache: Mesh header for ID " << mesh_params.getSculptID() << " - was retrieved from the cache." << LL_ENDL; @@ -1902,9 +1901,9 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) S32 disk_ofset = offset + CACHE_PREAMBLE_SIZE; //check cache for mesh asset LLFileSystem file(mesh_id, LLAssetType::AT_MESH); - if (in_cache && file.getSize() >= disk_ofset + size) + if (in_cache && (file.getSize() >= disk_ofset + size)) { - U8* buffer = new(std::nothrow) U8[size]; + U8* buffer = new(std::nothrow) U8[size]; // todo, make buffer thread local and read in thread? if (!buffer) { LL_WARNS(LOG_MESH) << "Can't allocate memory for mesh " << mesh_id << " LOD " << lod << ", size: " << size << LL_ENDL; @@ -1955,7 +1954,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); auto header_it = gMeshRepo.mThread->mMeshHeader.find(mesh_id); - if (header_it == gMeshRepo.mThread->mMeshHeader.end()) + if (header_it != gMeshRepo.mThread->mMeshHeader.end()) { LLMeshHeader& header = header_it->second; // for safety just mark everything as missing @@ -1971,11 +1970,13 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } } - S32 bytes = header_size + CACHE_PREAMBLE_SIZE; - LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); - if (file.getMaxSize() >= bytes) + if (header_size > 0) { - write_preamble(file, header_size, header_flags); + LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::READ_WRITE); + if (file.getMaxSize() >= CACHE_PREAMBLE_SIZE) + { + write_preamble(file, header_size, header_flags); + } } { -- cgit v1.2.3 From 587444bd279e7505a50ee9f2bbbe603c96d0214d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 02:29:10 +0200 Subject: #3488 mutex lock mSkinMap --- indra/newview/llmeshrepository.cpp | 33 ++++++++++++++++++++++----------- indra/newview/llmeshrepository.h | 1 + 2 files changed, 23 insertions(+), 11 deletions(-) (limited to 'indra/newview') 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.2.3 From 912c99ae96ce10da5916d24b5339f6bb2409dac6 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 04:18:14 +0200 Subject: #3488 Fix cache using wrong offset --- indra/newview/llmeshrepository.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index edffaae8b5..6c51ac23ac 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3706,7 +3706,7 @@ void LLMeshLODHandler::processLod(U8* data, S32 data_size) write_preamble(file, header_bytes, flags); } - file.seek(offset); + file.seek(offset, 0); file.write(data, size); LLMeshRepository::sCacheBytesWritten += size; ++LLMeshRepository::sCacheWrites; @@ -3828,7 +3828,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* write_preamble(file, header_bytes, flags); } - file.seek(offset); + file.seek(offset, 0); file.write(data, size); } } @@ -3903,7 +3903,7 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S write_preamble(file, header_bytes, flags); } - file.seek(offset); + file.seek(offset, 0); file.write(data, size); } } @@ -3976,7 +3976,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 write_preamble(file, header_bytes, flags); } - file.seek(offset); + file.seek(offset, 0); file.write(data, size); } } -- cgit v1.2.3 From 8c4abf8feb599a3c8f90019a2953a672efd3a09b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 08:01:53 +0200 Subject: #3488 Fix skin info not reading from cache --- indra/newview/llmeshrepository.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 6c51ac23ac..955558e56e 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3809,7 +3809,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* { LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); - LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); + LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); if (header_it != gMeshRepo.mThread->mMeshHeader.end()) { LLMeshHeader& header = header_it->second; @@ -3884,7 +3884,7 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S { LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); - LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); + LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); if (header_it != gMeshRepo.mThread->mMeshHeader.end()) { LLMeshHeader& header = header_it->second; @@ -3957,7 +3957,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 { LLMutexLock lock(gMeshRepo.mThread->mHeaderMutex); - LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshParams.getSculptID()); + LLMeshRepoThread::mesh_header_map::iterator header_it = gMeshRepo.mThread->mMeshHeader.find(mMeshID); if (header_it != gMeshRepo.mThread->mMeshHeader.end()) { LLMeshHeader& header = header_it->second; -- cgit v1.2.3 From 9bfdb7cf6ce05f4c2f97f82888a9aaae62298c42 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 08:32:38 +0200 Subject: #3488 #3488 Move skin info processing out of mesh thread Skin info parsing is expensive, offload to thread pool --- indra/newview/llmeshrepository.cpp | 138 ++++++++++++++++++++++++++++++++----- indra/newview/llmeshrepository.h | 2 +- 2 files changed, 121 insertions(+), 19 deletions(-) (limited to 'indra/newview') 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.2.3 From b84f3ff6b129cd71955bcb1fc885491b7002f87f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sun, 10 Nov 2024 12:21:34 +0200 Subject: viewer#2780 Speed up terrain loading on teleport Terrain was arriving and processing too late resulting in issues like "Lower terrain material incorrectly shown momentarily" --- indra/newview/llappviewer.cpp | 14 ++++++++++++++ indra/newview/llviewermessage.cpp | 3 ++- indra/newview/llviewertexturelist.cpp | 14 ++++++++++++-- 3 files changed, 28 insertions(+), 3 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 00624ebc6a..52bea875d9 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4785,6 +4785,20 @@ void LLAppViewer::idle() if (gTeleportDisplay) { + if (gAgent.getTeleportState() == LLAgent::TELEPORT_ARRIVING) + { + // Teleported, but waiting for things to load, start processing surface data + { + LL_RECORD_BLOCK_TIME(FTM_NETWORK); + gVLManager.unpackData(); + } + { + LL_RECORD_BLOCK_TIME(FTM_REGION_UPDATE); + const F32 max_region_update_time = .001f; // 1ms + LLWorld::getInstance()->updateRegions(max_region_update_time); + } + } + return; } diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1d4828fd33..0c702b24c1 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3147,7 +3147,8 @@ void send_agent_update(bool force_send, bool send_reliable) LL_PROFILE_ZONE_SCOPED; llassert(!gCubeSnapshot); - if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) + if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE + && gAgent.getTeleportState() != 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 diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 0b79c2d8e0..868dde05db 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -30,6 +30,7 @@ #include "llviewertexturelist.h" +#include "llagent.h" #include "llgl.h" // fot gathering stats from GL #include "llimagegl.h" #include "llimagebmp.h" @@ -815,10 +816,19 @@ void LLViewerTextureList::updateImages(F32 max_time) clearFetchingRequests(); gPipeline.clearRebuildGroups(); cleared = true; + return; } - return; + // ARRIVING is a delay to let things decode, cache and process, + // so process textures like normal despite gTeleportDisplay + if (gAgent.getTeleportState() != LLAgent::TELEPORT_ARRIVING) + { + return; + } + } + else + { + cleared = false; } - cleared = false; LLAppViewer::getTextureFetch()->setTextureBandwidth((F32)LLTrace::get_frame_recording().getPeriodMeanPerSec(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED).value()); -- cgit v1.2.3 From 74d2ed918dd185cbc47ed64f78be76ae6b94a60f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 4 Feb 2025 21:11:43 +0200 Subject: #3488 Speed up nearby avatar loading after a tp --- indra/newview/llmeshrepository.cpp | 6 +++++- indra/newview/llviewermessage.cpp | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) (limited to 'indra/newview') 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.2.3 From 6d0b0a77eed584c6bcf77f6cd5fdbdbf89a987d7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 18 Feb 2025 11:38:52 -0800 Subject: drain UDP socket to avoid dropped packets (#3565) drain UDP socket in idleNetwork() to avoid dropped packets --- indra/newview/llappviewer.cpp | 61 +++++++++++++++++-------------------------- indra/newview/llappviewer.h | 1 - indra/newview/llstartup.cpp | 15 ----------- 3 files changed, 24 insertions(+), 53 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 52bea875d9..ba474d3f39 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -350,8 +350,6 @@ LLVector3 gRelativeWindVec(0.0, 0.0, 0.0); U32 gPacketsIn = 0; -bool gPrintMessagesThisFrame = false; - bool gRandomizeFramerate = false; bool gPeriodicSlowFrame = false; @@ -1495,9 +1493,9 @@ bool LLAppViewer::doFrame() { LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df pauseMainloopTimeout"); - pingMainloopTimeout("Main:Sleep"); + pingMainloopTimeout("Main:Sleep"); - pauseMainloopTimeout(); + pauseMainloopTimeout(); } // Sleep and run background threads @@ -5217,12 +5215,9 @@ void LLAppViewer::idleNameCache() // Handle messages, and all message related stuff // -#define TIME_THROTTLE_MESSAGES -#ifdef TIME_THROTTLE_MESSAGES -#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!) +constexpr F32 CHECK_MESSAGES_DEFAULT_MAX_TIME = 0.020f; // 50 ms = 50 fps (just for messages!) static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; -#endif static LLTrace::BlockTimerStatHandle FTM_IDLE_NETWORK("Idle Network"); static LLTrace::BlockTimerStatHandle FTM_MESSAGE_ACKS("Message Acks"); @@ -5249,6 +5244,7 @@ void LLAppViewer::idleNetwork() F32 total_time = 0.0f; { + bool needs_drain = false; LockMessageChecker lmc(gMessageSystem); while (lmc.checkAllMessages(frame_count, gServicePump)) { @@ -5265,50 +5261,41 @@ void LLAppViewer::idleNetwork() if (total_decoded > MESSAGE_MAX_PER_FRAME) { + needs_drain = true; break; } -#ifdef TIME_THROTTLE_MESSAGES // Prevent slow packets from completely destroying the frame rate. // This usually happens due to clumps of avatars taking huge amount // of network processing time (which needs to be fixed, but this is // a good limit anyway). total_time = check_message_timer.getElapsedTimeF32(); if (total_time >= CheckMessagesMaxTime) + { + needs_drain = true; break; -#endif + } + } + if (needs_drain || gMessageSystem->mPacketRing.getNumBufferedPackets() > 0) + { + // Rather than allow packets to silently backup on the socket + // we drain them into our own buffer so we know how many exist. + S32 num_buffered_packets = gMessageSystem->drainUdpSocket(); + if (num_buffered_packets > 0) + { + // Increase CheckMessagesMaxTime so that we will eventually catch up + CheckMessagesMaxTime *= 1.035f; // 3.5% ~= 2x in 20 frames, ~8x in 60 frames + } + } + else + { + // Reset CheckMessagesMaxTime to default value + CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; } // Handle per-frame message system processing. lmc.processAcks(gSavedSettings.getF32("AckCollectTime")); } - -#ifdef TIME_THROTTLE_MESSAGES - if (total_time >= CheckMessagesMaxTime) - { - // Increase CheckMessagesMaxTime so that we will eventually catch up - CheckMessagesMaxTime *= 1.035f; // 3.5% ~= x2 in 20 frames, ~8x in 60 frames - } - else - { - // Reset CheckMessagesMaxTime to default value - CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; - } -#endif - - // Decode enqueued messages... - S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded; - - if( remaining_possible_decodes <= 0 ) - { - LL_INFOS() << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << LL_ENDL; - } - - if (gPrintMessagesThisFrame) - { - LL_INFOS() << "Decoded " << total_decoded << " msgs this frame!" << LL_ENDL; - gPrintMessagesThisFrame = false; - } } add(LLStatViewer::NUM_NEW_OBJECTS, gObjectList.mNumNewObjects); diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 4ce4259ed8..a253f06d14 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -400,7 +400,6 @@ extern std::string gLastVersionChannel; extern LLVector3 gWindVec; extern LLVector3 gRelativeWindVec; extern U32 gPacketsIn; -extern bool gPrintMessagesThisFrame; extern bool gRandomizeFramerate; extern bool gPeriodicSlowFrame; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index b32b80331a..5f6c5e1e15 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -623,21 +623,6 @@ bool idle_startup() F32 dropPercent = gSavedSettings.getF32("PacketDropPercentage"); msg->mPacketRing.setDropPercentage(dropPercent); - - F32 inBandwidth = gSavedSettings.getF32("InBandwidth"); - F32 outBandwidth = gSavedSettings.getF32("OutBandwidth"); - if (inBandwidth != 0.f) - { - LL_DEBUGS("AppInit") << "Setting packetring incoming bandwidth to " << inBandwidth << LL_ENDL; - msg->mPacketRing.setUseInThrottle(true); - msg->mPacketRing.setInBandwidth(inBandwidth); - } - if (outBandwidth != 0.f) - { - LL_DEBUGS("AppInit") << "Setting packetring outgoing bandwidth to " << outBandwidth << LL_ENDL; - msg->mPacketRing.setUseOutThrottle(true); - msg->mPacketRing.setOutBandwidth(outBandwidth); - } } LL_INFOS("AppInit") << "Message System Initialized." << LL_ENDL; -- cgit v1.2.3 From 483e85cbf31e08e3692d2fb267bdaacdd0ed38a4 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 7 Feb 2025 23:24:33 +0200 Subject: #3488 Prioritization adjustments --- indra/newview/llmeshrepository.cpp | 40 ++++++++++++++++++++++---------------- 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') 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.2.3 From 29b13af84b6092f2368ef8ebfd6431003a963c10 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 22 Feb 2025 10:41:11 +0200 Subject: #3596 Ensure correct loading mesh gets checked and removed --- indra/newview/llmeshrepository.cpp | 35 +++++++++++++++++------------------ indra/newview/llmeshrepository.h | 8 ++++---- 2 files changed, 21 insertions(+), 22 deletions(-) (limited to 'indra/newview') 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.2.3 From 1503e84602f91bef31a5c414852e480805e613b4 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 22 Feb 2025 10:42:28 +0200 Subject: #3596 Fix mMutex in mesh thread --- indra/newview/llmeshrepository.cpp | 50 +++++++++++++++++++++++++++++++------- indra/newview/llmeshrepository.h | 1 + 2 files changed, 42 insertions(+), 9 deletions(-) (limited to 'indra/newview') 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.2.3 From f75968a32878a56d579b911a743f6d7842e6593b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 22 Feb 2025 10:43:35 +0200 Subject: #3596 Convert some mesh repository functions to const --- indra/newview/llmeshrepository.cpp | 38 +++++++++++++++++++------------------- indra/newview/llmeshrepository.h | 26 +++++++++++++------------- 2 files changed, 32 insertions(+), 32 deletions(-) (limited to 'indra/newview') 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.2.3 From 100861aec17239543b21b3356ed2d8d009ba77ac Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Sat, 22 Feb 2025 11:02:45 +0200 Subject: #3596 Fix use of dead references 1. Was posting a would-be-dead reference to a thread 2. loadMeshLODs was getting dead references. It's no longer relevant so just restored logic to wotk like it was before loadMeshLODs --- indra/newview/llmeshrepository.cpp | 83 +++++++------------------------------- indra/newview/llmeshrepository.h | 6 +-- 2 files changed, 16 insertions(+), 73 deletions(-) (limited to 'indra/newview') 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.2.3 From b52842ee2a328d80834f9a62c1b130ec757b9a03 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 26 Feb 2025 10:24:19 -0800 Subject: theoretical earlier start to asset downloads during startup (#3589) * process UDP packets earlier in startup sequence * call lazyAttach() when nonzero pending attachments --- indra/newview/llappviewer.cpp | 5 +- indra/newview/llappviewer.h | 2 +- indra/newview/llstartup.cpp | 272 +++++++++++++++++++++++------------------- indra/newview/llvoavatar.cpp | 5 +- 4 files changed, 153 insertions(+), 131 deletions(-) (limited to 'indra/newview') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 241dbeaae5..18ad19f6bc 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -349,8 +349,6 @@ std::string gLastVersionChannel; LLVector3 gWindVec(3.0, 3.0, 0.0); LLVector3 gRelativeWindVec(0.0, 0.0, 0.0); -U32 gPacketsIn = 0; - bool gRandomizeFramerate = false; bool gPeriodicSlowFrame = false; @@ -359,6 +357,7 @@ bool gLLErrorActivated = false; bool gLogoutInProgress = false; bool gSimulateMemLeak = false; +bool gDoDisconnect = false; // We don't want anyone, especially threads working on the graphics pipeline, // to have to block due to this WorkQueue being full. @@ -372,7 +371,6 @@ const std::string MARKER_FILE_NAME("SecondLife.exec_marker"); const std::string START_MARKER_FILE_NAME("SecondLife.start_marker"); const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker"); const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker"); -static bool gDoDisconnect = false; static std::string gLaunchFileOnQuit; // Used on Win32 for other apps to identify our window (eg, win_setup) @@ -5393,7 +5391,6 @@ void LLAppViewer::idleNetwork() } total_decoded++; - gPacketsIn++; if (total_decoded > MESSAGE_MAX_PER_FRAME) { diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 832d61101a..ad42c4a6f1 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -411,10 +411,10 @@ extern std::string gLastVersionChannel; extern LLVector3 gWindVec; extern LLVector3 gRelativeWindVec; -extern U32 gPacketsIn; extern bool gRandomizeFramerate; extern bool gPeriodicSlowFrame; +extern bool gDoDisconnect; extern bool gSimulateMemLeak; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 5cd27352de..eb0e9ef4bc 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -297,16 +297,62 @@ void callback_cache_name(const LLUUID& id, const std::string& full_name, bool is // exported functionality // +void do_startup_frame() +{ + // Until after STATE_AGENT_SEND we don't get very many UDP packets to poll the socket, + // and after STATE_PRECACHE the LLAppViewer::idleNetwork() will do UDP processing, + // so we only bother to process between those two states. + EStartupState state = LLStartUp::getStartupState(); + if (state > STATE_AGENT_SEND && state < STATE_PRECACHE) + { + // drain the UDP socket... + U64 t0 = totalTime(); + constexpr U64 MAX_STARTUP_FRAME_TIME = 2000; // usec + constexpr U64 MAX_STARTUP_FRAME_MESSAGES = 100; + S32 num_messages = 0; + bool needs_drain = false; + LockMessageChecker lmc(gMessageSystem); + while (lmc.checkAllMessages(gFrameCount, gServicePump)) + { + if (gDoDisconnect) + { + // We're disconnecting, don't process any more messages from the server + // We're usually disconnecting due to either network corruption or a + // server going down, so this is OK. + break; + } + if (++num_messages >= MAX_STARTUP_FRAME_MESSAGES + || (totalTime() - t0) > MAX_STARTUP_FRAME_TIME) + { + needs_drain = true; + break; + } + } + if (needs_drain || gMessageSystem->mPacketRing.getNumBufferedPackets() > 0) + { + gMessageSystem->drainUdpSocket(); + } + lmc.processAcks(); + } + // ...then call display_startup() + display_startup(); +} + void pump_idle_startup_network(void) { + // while there are message to process: + // process one then call display_startup() + S32 num_messages = 0; { LockMessageChecker lmc(gMessageSystem); while (lmc.checkAllMessages(gFrameCount, gServicePump)) { display_startup(); + ++num_messages; } lmc.processAcks(); } + // finally call one last display_startup() display_startup(); } @@ -728,7 +774,7 @@ bool idle_startup() LL_DEBUGS("AppInit") << "STATE_BROWSER_INIT" << LL_ENDL; std::string msg = LLTrans::getString("LoginInitializingBrowser"); set_startup_status(0.03f, msg.c_str(), gAgent.mMOTD.c_str()); - display_startup(); + do_startup_frame(); // LLViewerMedia::initBrowser(); LLStartUp::setStartupState( STATE_LOGIN_SHOW ); return false; @@ -793,7 +839,7 @@ bool idle_startup() LL_DEBUGS("AppInit") << "FirstLoginThisInstall off" << LL_ENDL; } } - display_startup(); + do_startup_frame(); LLStartUp::setStartupState( STATE_LOGIN_WAIT ); // Wait for user input } else @@ -824,7 +870,7 @@ bool idle_startup() } LL_DEBUGS("AppInit") << "PeekMessage processed" << LL_ENDL; #endif - display_startup(); + do_startup_frame(); timeout.reset(); return false; } @@ -839,7 +885,7 @@ bool idle_startup() // Don't do anything. Wait for the login view to call the login_callback, // which will push us to the next state. - // display() function will be the one to run display_startup() + // display() function will be the one to run do_startup_frame() // Sleep so we don't spin the CPU ms_sleep(1); return false; @@ -1021,7 +1067,7 @@ bool idle_startup() auth_desc = LLTrans::getString("LoginInProgress"); set_startup_status(progress, auth_desc, auth_message); progress += 0.02f; - display_startup(); + do_startup_frame(); // Setting initial values... LLLoginInstance* login = LLLoginInstance::getInstance(); @@ -1058,7 +1104,7 @@ bool idle_startup() emsg << LLTrans::getString("LoginFailedHeader") << "\n"; if(LLLoginInstance::getInstance()->authFailure()) { - LL_INFOS("LLStartup") << "Login failed, LLLoginInstance::getResponse(): " + LL_INFOS("LLStartUp") << "Login failed, LLLoginInstance::getResponse(): " << LLLoginInstance::getInstance()->getResponse() << LL_ENDL; LLSD response = LLLoginInstance::getInstance()->getResponse(); // Still have error conditions that may need some @@ -1130,7 +1176,7 @@ bool idle_startup() // If optional was skipped this case shouldn't // be reached. - LL_INFOS("LLStartup") << "Forcing a quit due to update." << LL_ENDL; + LL_INFOS("LLStartUp") << "Forcing a quit due to update." << LL_ENDL; LLLoginInstance::getInstance()->disconnect(); LLAppViewer::instance()->forceQuit(); } @@ -1158,7 +1204,7 @@ bool idle_startup() } catch (LLCertException &cert_exception) { - LL_WARNS("LLStartup", "SECAPI") << "Caught " << cert_exception.what() << " certificate expception on getCertificate("<< response["certificate"] << ")" << LL_ENDL; + LL_WARNS("LLStartUp", "SECAPI") << "Caught " << cert_exception.what() << " certificate expception on getCertificate("<< response["certificate"] << ")" << LL_ENDL; LLSD args; args["REASON"] = LLTrans::getString(cert_exception.what()); @@ -1210,7 +1256,7 @@ bool idle_startup() // notificatioin message. LLSD args; args["ERROR_MESSAGE"] = emsg.str(); - LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; + LL_INFOS("LLStartUp") << "Notification: " << args << LL_ENDL; LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); } } @@ -1233,7 +1279,7 @@ bool idle_startup() { LLSD args; args["ERROR_MESSAGE"] = emsg.str(); - LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; + LL_INFOS("LLStartUp") << "Notification: " << args << LL_ENDL; LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); transition_back_to_login_panel(emsg.str()); show_connect_box = true; @@ -1249,71 +1295,71 @@ bool idle_startup() if (STATE_WORLD_INIT == LLStartUp::getStartupState()) { set_startup_status(0.30f, LLTrans::getString("LoginInitializingWorld"), gAgent.mMOTD); - display_startup(); + do_startup_frame(); // We should have an agent id by this point. llassert(!(gAgentID == LLUUID::null)); // Finish agent initialization. (Requires gSavedSettings, builds camera) gAgent.init(); - display_startup(); + do_startup_frame(); gAgentCamera.init(); - display_startup(); - display_startup(); + do_startup_frame(); + do_startup_frame(); // Since we connected, save off the settings so the user doesn't have to // type the name/password again if we crash. gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), true); LLUIColorTable::instance().saveUserSettings(); - display_startup(); + do_startup_frame(); // // Initialize classes w/graphics stuff. // LLSurface::initClasses(); - display_startup(); + do_startup_frame(); - display_startup(); + do_startup_frame(); LLDrawable::initClass(); - display_startup(); + do_startup_frame(); // init the shader managers LLPostProcess::initClass(); - display_startup(); + do_startup_frame(); LLAvatarAppearance::initClass("avatar_lad.xml","avatar_skeleton.xml"); - display_startup(); + do_startup_frame(); LLViewerObject::initVOClasses(); - display_startup(); + do_startup_frame(); // Initialize all our tools. Must be done after saved settings loaded. // NOTE: This also is where gToolMgr used to be instantiated before being turned into a singleton. LLToolMgr::getInstance()->initTools(); - display_startup(); + do_startup_frame(); // Pre-load floaters, like the world map, that are slow to spawn // due to XML complexity. gViewerWindow->initWorldUI(); - display_startup(); + do_startup_frame(); // This is where we used to initialize gWorldp. Original comment said: // World initialization must be done after above window init // User might have overridden far clip LLWorld::getInstance()->setLandFarClip(gAgentCamera.mDrawDistance); - display_startup(); + do_startup_frame(); // Before we create the first region, we need to set the agent's mOriginGlobal // This is necessary because creating objects before this is set will result in a // bad mPositionAgent cache. gAgent.initOriginGlobal(from_region_handle(gFirstSimHandle)); - display_startup(); + do_startup_frame(); LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim); - display_startup(); + do_startup_frame(); LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(gFirstSimHandle); LL_INFOS("AppInit") << "Adding initial simulator " << regionp->getOriginGlobal() << LL_ENDL; @@ -1322,18 +1368,18 @@ bool idle_startup() << gFirstSimSeedCap << LL_ENDL; regionp->setSeedCapability(gFirstSimSeedCap); LL_DEBUGS("AppInit") << "Waiting for seed grant ...." << LL_ENDL; - display_startup(); + do_startup_frame(); // Set agent's initial region to be the one we just created. gAgent.setRegion(regionp); - display_startup(); + do_startup_frame(); // Set agent's initial position, which will be read by LLVOAvatar when the avatar // object is created. I think this must be done after setting the region. JC gAgent.setPositionAgent(agent_start_position_region); - display_startup(); + do_startup_frame(); LLStartUp::initExperiences(); - display_startup(); + do_startup_frame(); // If logging should be enebled, turns it on and loads history from disk // Note: does not happen on init of singleton because preferences can use @@ -1354,7 +1400,7 @@ bool idle_startup() { LLStartUp::multimediaInit(); LLStartUp::setStartupState( STATE_FONT_INIT ); - display_startup(); + do_startup_frame(); return false; } @@ -1363,7 +1409,7 @@ bool idle_startup() { LLStartUp::fontInit(); LLStartUp::setStartupState( STATE_SEED_GRANTED_WAIT ); - display_startup(); + do_startup_frame(); return false; } @@ -1435,7 +1481,7 @@ bool idle_startup() set_startup_status(0.4f, LLTrans::getString("LoginRequestSeedCapGrant"), gAgent.mMOTD.c_str()); } } - display_startup(); + do_startup_frame(); return false; } @@ -1446,7 +1492,7 @@ bool idle_startup() //--------------------------------------------------------------------- if (STATE_SEED_CAP_GRANTED == LLStartUp::getStartupState()) { - display_startup(); + do_startup_frame(); // These textures are not warrantied to be cached, so needs // to hapen with caps granted @@ -1455,9 +1501,9 @@ bool idle_startup() // will init images, should be done with caps, but before gSky.init() LLEnvironment::getInstance()->initSingleton(); - display_startup(); + do_startup_frame(); update_texture_fetch(); - display_startup(); + do_startup_frame(); if ( gViewerWindow != NULL) { // This isn't the first logon attempt, so show the UI @@ -1465,15 +1511,15 @@ bool idle_startup() } gLoginMenuBarView->setVisible( false ); gLoginMenuBarView->setEnabled( false ); - display_startup(); + do_startup_frame(); // direct logging to the debug console's line buffer LLError::logToFixedBuffer(gDebugView->mDebugConsolep); - display_startup(); + do_startup_frame(); // set initial visibility of debug console gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole")); - display_startup(); + do_startup_frame(); // // Set message handlers @@ -1482,23 +1528,23 @@ bool idle_startup() // register callbacks for messages. . . do this after initial handshake to make sure that we don't catch any unwanted register_viewer_callbacks(gMessageSystem); - display_startup(); + do_startup_frame(); // Debugging info parameters gMessageSystem->setMaxMessageTime( 0.5f ); // Spam if decoding all msgs takes more than 500 ms - display_startup(); + do_startup_frame(); #ifndef LL_RELEASE_FOR_DOWNLOAD gMessageSystem->setTimeDecodes( true ); // Time the decode of each msg gMessageSystem->setTimeDecodesSpamThreshold( 0.05f ); // Spam if a single msg takes over 50ms to decode #endif - display_startup(); + do_startup_frame(); gXferManager->registerCallbacks(gMessageSystem); - display_startup(); + do_startup_frame(); LLStartUp::initNameCache(); - display_startup(); + do_startup_frame(); // update the voice settings *after* gCacheName initialization // so that we can construct voice UI that relies on the name cache @@ -1506,7 +1552,7 @@ bool idle_startup() { LLVoiceClient::getInstance()->updateSettings(); } - display_startup(); + do_startup_frame(); // create a container's instance for start a controlling conversation windows // by the voice's events @@ -1527,12 +1573,12 @@ bool idle_startup() // register null callbacks for audio until the audio system is initialized gMessageSystem->setHandlerFuncFast(_PREHASH_SoundTrigger, null_message_callback, NULL); gMessageSystem->setHandlerFuncFast(_PREHASH_AttachedSound, null_message_callback, NULL); - display_startup(); + do_startup_frame(); //reset statistics LLViewerStats::instance().resetStats(); - display_startup(); + do_startup_frame(); // // Set up region and surface defaults // @@ -1557,7 +1603,7 @@ bool idle_startup() LLViewerCamera::getInstance()->setAspect(gViewerWindow->getWorldViewAspectRatio()); // Initialize FOV LLViewerCamera::getInstance()->setDefaultFOV(gSavedSettings.getF32("CameraAngle")); - display_startup(); + do_startup_frame(); // Move agent to starting location. The position handed to us by // the space server is in global coordinates, but the agent frame @@ -1568,7 +1614,7 @@ bool idle_startup() gAgent.resetAxes(gAgentStartLookAt); gAgentCamera.stopCameraAnimation(); gAgentCamera.resetCamera(); - display_startup(); + do_startup_frame(); // Initialize global class data needed for surfaces (i.e. textures) LL_DEBUGS("AppInit") << "Initializing sky..." << LL_ENDL; @@ -1579,7 +1625,7 @@ bool idle_startup() LLGLState::checkStates(); - display_startup(); + do_startup_frame(); LL_DEBUGS("AppInit") << "Decoding images..." << LL_ENDL; // For all images pre-loaded into viewer cache, init @@ -1593,12 +1639,12 @@ bool idle_startup() { F32 frac = (F32)i / (F32)DECODE_TIME_SEC; set_startup_status(0.45f + frac*0.1f, LLTrans::getString("LoginDecodingImages"), gAgent.mMOTD); - display_startup(); + do_startup_frame(); gTextureList.decodeAllImages(1.f); } LLStartUp::setStartupState( STATE_WORLD_WAIT ); - display_startup(); + do_startup_frame(); // JC - Do this as late as possible to increase likelihood Purify // will run. @@ -1627,7 +1673,7 @@ bool idle_startup() NULL); timeout.reset(); - display_startup(); + do_startup_frame(); return false; } @@ -1654,7 +1700,7 @@ bool idle_startup() { LL_DEBUGS("AppInit") << "Connecting to region..." << LL_ENDL; set_startup_status(0.60f, LLTrans::getString("LoginConnectingToRegion"), gAgent.mMOTD); - display_startup(); + do_startup_frame(); // register with the message system so it knows we're // expecting this message LLMessageSystem* msg = gMessageSystem; @@ -1668,7 +1714,7 @@ bool idle_startup() gAssetStorage->setUpstream(regionp->getHost()); gCacheName->setUpstream(regionp->getHost()); } - display_startup(); + do_startup_frame(); // Create login effect // But not on first login, because you can't see your avatar then @@ -1683,7 +1729,7 @@ bool idle_startup() LLStartUp::setStartupState( STATE_AGENT_WAIT ); // Go to STATE_AGENT_WAIT timeout.reset(); - display_startup(); + do_startup_frame(); return false; } @@ -1692,35 +1738,13 @@ bool idle_startup() //--------------------------------------------------------------------- if (STATE_AGENT_WAIT == LLStartUp::getStartupState()) { - { - LockMessageChecker lmc(gMessageSystem); - while (lmc.checkAllMessages(gFrameCount, gServicePump)) - { - if (gAgentMovementCompleted) - { - // Sometimes we have more than one message in the - // queue. break out of this loop and continue - // processing. If we don't, then this could skip one - // or more login steps. - break; - } - else - { - LL_DEBUGS("AppInit") << "Awaiting AvatarInitComplete, got " - << gMessageSystem->getMessageName() << LL_ENDL; - } - display_startup(); - } - lmc.processAcks(); - } - - display_startup(); + do_startup_frame(); if (gAgentMovementCompleted) { LLStartUp::setStartupState( STATE_INVENTORY_SEND ); } - display_startup(); + do_startup_frame(); if (!gAgentMovementCompleted && timeout.getElapsedTimeF32() > STATE_AGENT_WAIT_TIMEOUT) { @@ -1753,7 +1777,7 @@ bool idle_startup() if (STATE_INVENTORY_SEND == LLStartUp::getStartupState()) { LL_PROFILE_ZONE_NAMED("State inventory send") - display_startup(); + do_startup_frame(); // request mute list LL_INFOS() << "Requesting Mute List" << LL_ENDL; @@ -1763,12 +1787,12 @@ bool idle_startup() LL_INFOS() << "Requesting Money Balance" << LL_ENDL; LLStatusBar::sendMoneyBalanceRequest(); - display_startup(); + do_startup_frame(); // Inform simulator of our language preference LLAgentLanguage::update(); - display_startup(); + do_startup_frame(); // unpack thin inventory LLSD response = LLLoginInstance::getInstance()->getResponse(); //bool dump_buffer = false; @@ -1783,7 +1807,7 @@ bool idle_startup() gInventory.setLibraryRootFolderID(id.asUUID()); } } - display_startup(); + do_startup_frame(); LLSD inv_lib_owner = response["inventory-lib-owner"]; if(inv_lib_owner.isDefined()) @@ -1795,9 +1819,9 @@ bool idle_startup() gInventory.setLibraryOwnerID(LLUUID(id.asUUID())); } } - display_startup(); + do_startup_frame(); LLStartUp::setStartupState(STATE_INVENTORY_SKEL); - display_startup(); + do_startup_frame(); return false; } @@ -1816,7 +1840,7 @@ bool idle_startup() LL_WARNS("AppInit") << "Problem loading inventory-skel-lib" << LL_ENDL; } } - display_startup(); + do_startup_frame(); LLSD inv_skeleton = response["inventory-skeleton"]; if (inv_skeleton.isDefined()) @@ -1827,9 +1851,9 @@ bool idle_startup() LL_WARNS("AppInit") << "Problem loading inventory-skel-targets" << LL_ENDL; } } - display_startup(); + do_startup_frame(); LLStartUp::setStartupState(STATE_INVENTORY_SEND2); - display_startup(); + do_startup_frame(); return false; } @@ -1875,7 +1899,7 @@ bool idle_startup() list[agent_id] = new LLRelationship(given_rights, has_rights, false); } LLAvatarTracker::instance().addBuddyList(list); - display_startup(); + do_startup_frame(); } bool show_hud = false; @@ -1903,7 +1927,7 @@ bool idle_startup() //} } } - display_startup(); + do_startup_frame(); // Either we want to show tutorial because this is the first login // to a Linden Help Island or the user quit with the tutorial @@ -1912,21 +1936,21 @@ bool idle_startup() { LLFloaterReg::showInstance("hud", LLSD(), false); } - display_startup(); + do_startup_frame(); LLSD event_notifications = response["event_notifications"]; if(event_notifications.isDefined()) { gEventNotifier.load(event_notifications); } - display_startup(); + do_startup_frame(); LLSD classified_categories = response["classified_categories"]; if(classified_categories.isDefined()) { LLClassifiedInfo::loadCategories(classified_categories); } - display_startup(); + do_startup_frame(); // This method MUST be called before gInventory.findCategoryUUIDForType because of // gInventory.mIsAgentInvUsable is set to true in the gInventory.buildParentChildMap. @@ -1945,7 +1969,7 @@ bool idle_startup() LLInventoryModelBackgroundFetch::instance().start(); gInventory.createCommonSystemCategories(); LLStartUp::setStartupState(STATE_INVENTORY_CALLBACKS ); - display_startup(); + do_startup_frame(); return false; } @@ -1957,7 +1981,7 @@ bool idle_startup() { if (!LLInventoryModel::isSysFoldersReady()) { - display_startup(); + do_startup_frame(); return false; } @@ -1982,7 +2006,7 @@ bool idle_startup() gInventory.addChangedMask(LLInventoryObserver::ALL, LLUUID::null); gInventory.notifyObservers(); - display_startup(); + do_startup_frame(); // set up callbacks LL_INFOS() << "Registering Callbacks" << LL_ENDL; @@ -1993,18 +2017,18 @@ bool idle_startup() LLAvatarTracker::instance().registerCallbacks(msg); LL_INFOS() << " Landmark" << LL_ENDL; LLLandmark::registerCallbacks(msg); - display_startup(); + do_startup_frame(); // request all group information LL_INFOS() << "Requesting Agent Data" << LL_ENDL; gAgent.sendAgentDataUpdateRequest(); - display_startup(); + do_startup_frame(); // Create the inventory views LL_INFOS() << "Creating Inventory Views" << LL_ENDL; LLFloaterReg::getInstance("inventory"); - display_startup(); + do_startup_frame(); LLStartUp::setStartupState( STATE_MISC ); - display_startup(); + do_startup_frame(); return false; } @@ -2054,7 +2078,7 @@ bool idle_startup() gSavedSettings.setBOOL("ShowStartLocation", true); } - display_startup(); + do_startup_frame(); // Load stored local environment if needed. LLEnvironment::instance().loadFromSettings(); @@ -2062,7 +2086,7 @@ bool idle_startup() // *TODO : Uncomment that line once the whole grid migrated to SLM and suppress it from LLAgent::handleTeleportFinished() (llagent.cpp) //check_merchant_status(); - display_startup(); + do_startup_frame(); if (gSavedSettings.getBOOL("HelpFloaterOpen")) { @@ -2070,7 +2094,7 @@ bool idle_startup() LLViewerHelp::instance().showTopic(""); } - display_startup(); + do_startup_frame(); // We're successfully logged in. gSavedSettings.setBOOL("FirstLoginThisInstall", false); @@ -2079,12 +2103,12 @@ bool idle_startup() LLFloaterGridStatus::getInstance()->startGridStatusTimer(); - display_startup(); + do_startup_frame(); - display_startup(); + do_startup_frame(); // JC: Initializing audio requests many sounds for download. init_audio(); - display_startup(); + do_startup_frame(); // JC: Initialize "active" gestures. This may also trigger // many gesture downloads, if this is the user's first @@ -2122,7 +2146,7 @@ bool idle_startup() LLGestureMgr::instance().startFetch(); } gDisplaySwapBuffers = true; - display_startup(); + do_startup_frame(); LLMessageSystem* msg = gMessageSystem; msg->setHandlerFuncFast(_PREHASH_SoundTrigger, process_sound_trigger); @@ -2200,10 +2224,10 @@ bool idle_startup() } } - display_startup(); + do_startup_frame(); //DEV-17797. get null folder. Any items found here moved to Lost and Found LLInventoryModelBackgroundFetch::instance().findLostItems(); - display_startup(); + do_startup_frame(); LLStartUp::setStartupState( STATE_PRECACHE ); timeout.reset(); @@ -2212,7 +2236,7 @@ bool idle_startup() if (STATE_PRECACHE == LLStartUp::getStartupState()) { - display_startup(); + do_startup_frame(); F32 timeout_frac = timeout.getElapsedTimeF32()/PRECACHING_DELAY; // We now have an inventory skeleton, so if this is a user's first @@ -2238,7 +2262,7 @@ bool idle_startup() callAfterCOFFetch(set_flags_and_update_appearance); } - display_startup(); + do_startup_frame(); // wait precache-delay and for agent's avatar or a lot longer. if ((timeout_frac > 1.f) && isAgentAvatarValid()) @@ -2263,7 +2287,7 @@ bool idle_startup() set_startup_status(0.60f + 0.30f * timeout_frac, LLTrans::getString("LoginPrecaching"), gAgent.mMOTD.c_str()); - display_startup(); + do_startup_frame(); } return true; @@ -2291,7 +2315,7 @@ bool idle_startup() LLStartUp::setStartupState( STATE_CLEANUP ); } - display_startup(); + do_startup_frame(); if (gAgent.isOutfitChosen() && (wearables_time > MAX_WEARABLES_TIME)) { @@ -2332,7 +2356,7 @@ bool idle_startup() if (STATE_CLEANUP == LLStartUp::getStartupState()) { set_startup_status(1.0, "", ""); - display_startup(); + do_startup_frame(); if (!mBenefitsSuccessfullyInit) { @@ -2353,7 +2377,7 @@ bool idle_startup() //gViewerWindow->revealIntroPanel(); gViewerWindow->setStartupComplete(); gViewerWindow->setProgressCancelButtonVisible(false); - display_startup(); + do_startup_frame(); // We're not away from keyboard, even though login might have taken // a while. JC @@ -2385,7 +2409,7 @@ bool idle_startup() // LLUserAuth::getInstance()->reset(); LLStartUp::setStartupState( STATE_STARTED ); - display_startup(); + do_startup_frame(); // Unmute audio if desired and setup volumes. // This is a not-uncommon crash site, so surround it with @@ -2401,7 +2425,7 @@ bool idle_startup() LLAgentPicksInfo::getInstance()->requestNumberOfPicks(); - display_startup(); + do_startup_frame(); llassert(LLPathfindingManager::getInstance() != NULL); LLPathfindingManager::getInstance()->initSystem(); @@ -3006,9 +3030,7 @@ std::string LLStartUp::startupStateToString(EStartupState state) // static void LLStartUp::setStartupState( EStartupState state ) { - LL_INFOS("AppInit") << "Startup state changing from " << - getStartupStateString() << " to " << - startupStateToString(state) << LL_ENDL; + LL_INFOS("AppInit") << getStartupStateString() << " --> " << startupStateToString(state) << LL_ENDL; getPhases().stopPhase(getStartupStateString()); gStartupState = state; @@ -3072,7 +3094,7 @@ void LLStartUp::multimediaInit() LL_DEBUGS("AppInit") << "Initializing Multimedia...." << LL_ENDL; std::string msg = LLTrans::getString("LoginInitializingMultimedia"); set_startup_status(0.42f, msg.c_str(), gAgent.mMOTD.c_str()); - display_startup(); + do_startup_frame(); } void LLStartUp::fontInit() @@ -3080,7 +3102,7 @@ void LLStartUp::fontInit() LL_DEBUGS("AppInit") << "Initializing fonts...." << LL_ENDL; std::string msg = LLTrans::getString("LoginInitializingFonts"); set_startup_status(0.45f, msg.c_str(), gAgent.mMOTD.c_str()); - display_startup(); + do_startup_frame(); LLFontGL::loadDefaultFonts(); } @@ -3777,14 +3799,14 @@ bool process_login_success_response() { // We got an answer from the grid -> use that for map for the current session gSavedSettings.setString("CurrentMapServerURL", map_server_url); - LL_INFOS("LLStartup") << "map-server-url : we got an answer from the grid : " << map_server_url << LL_ENDL; + LL_INFOS("LLStartUp") << "map-server-url : we got an answer from the grid : " << map_server_url << LL_ENDL; } else { // No answer from the grid -> use the default setting for current session map_server_url = gSavedSettings.getString("MapServerURL"); gSavedSettings.setString("CurrentMapServerURL", map_server_url); - LL_INFOS("LLStartup") << "map-server-url : no map-server-url answer, we use the default setting for the map : " << map_server_url << LL_ENDL; + LL_INFOS("LLStartUp") << "map-server-url : no map-server-url answer, we use the default setting for the map : " << map_server_url << LL_ENDL; } // Default male and female avatars allowing the user to choose their avatar on first login. diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 51991c1261..40312b7f4e 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -2794,7 +2794,10 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, const F64 &time) } // attach objects that were waiting for a drawable - lazyAttach(); + if (!mPendingAttachment.empty()) + { + lazyAttach(); + } // animate the character // store off last frame's root position to be consistent with camera position -- cgit v1.2.3