#3960 Fix expensive mesh thread score calculations

master
Andrey Kleshchev 2025-04-22 19:45:48 +03:00 committed by Andrey Kleshchev
parent 87b6428f55
commit b30283e5ef
2 changed files with 145 additions and 112 deletions

View File

@ -544,6 +544,66 @@ bool RequestStats::isDelayed() const
return mTimer.getStarted() && !mTimer.hasExpired();
}
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();
if (!avatar->isSelf() && !avatar->hasFirstFullAttachmentData())
{
// slightly deprioritize avatars that are still receiving data
radius *= 0.9f;
}
}
return radius / llmax(av_drawable->mDistanceWRTCamera, 1.f);
}
}
return drawable->getRadius() / llmax(drawable->mDistanceWRTCamera, 1.f);
}
void PendingRequestBase::updateScore()
{
mScore = 0;
if (mTrackedData)
{
for (LLVOVolume* volume : mTrackedData->mVolumes)
{
F32 cur_score = calculate_score(volume);
if (cur_score > 0)
{
mScore = llmax(mScore, cur_score);
}
}
}
}
LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMaterial& material)
{
LLPointer< LLViewerFetchedTexture > * ppTex = static_cast< LLPointer< LLViewerFetchedTexture > * >(material.mOpaqueData);
@ -2220,7 +2280,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes
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
gMeshRepo.mLoadingSkins[mesh_id]; // add an empty vector to indicate to main thread that we are loading skin info
}
}
@ -4212,13 +4272,13 @@ void LLMeshRepository::unregisterMesh(LLVOVolume* vobj)
{
for (auto& param : lod)
{
vector_replace_with_last(param.second, vobj);
vector_replace_with_last(param.second.mVolumes, vobj);
}
}
for (auto& skin_pair : mLoadingSkins)
{
vector_replace_with_last(skin_pair.second, vobj);
vector_replace_with_last(skin_pair.second.mVolumes, vobj);
}
}
@ -4241,16 +4301,17 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
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()) {
iter->second.push_back(vobj);
auto it = std::find(iter->second.mVolumes.begin(), iter->second.mVolumes.end(), vobj);
if (it == iter->second.mVolumes.end()) {
iter->second.addVolume(vobj);
}
}
else
{
//first request for this mesh
mLoadingMeshes[new_lod][mesh_id].push_back(vobj);
mPendingRequests.emplace_back(new PendingRequestLOD(mesh_params, new_lod));
std::shared_ptr<PendingRequestBase> request(new PendingRequestLOD(mesh_params, new_lod));
mPendingRequests.emplace_back(request);
mLoadingMeshes[new_lod][mesh_id].initData(vobj, request);
LLMeshRepository::sLODPending++;
}
}
@ -4309,50 +4370,6 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para
return new_lod;
}
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();
if (!avatar->isSelf() && !avatar->hasFirstFullAttachmentData())
{
// slightly deprioritize avatars that are still receiving data
radius *= 0.9f;
}
}
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);
@ -4549,51 +4566,14 @@ void LLMeshRepository::notifyLoadedMeshes()
if (mPendingRequests.size() > push_count)
{
LL_PROFILE_ZONE_NAMED("Mesh score_map");
LL_PROFILE_ZONE_NAMED("Mesh score update");
// More requests than the high-water limit allows so
// sort and forward the most important.
//calculate "score" for pending requests
//create score map
std::map<LLUUID, F32> score_map;
for (U32 i = 0; i < LLVolumeLODGroup::NUM_LODS; ++i)
// update "score" for pending requests
for (std::shared_ptr<PendingRequestBase>& req_p : mPendingRequests)
{
for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter)
{
F32 max_score = 0.f;
for (auto obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)
{
F32 cur_score = calculate_score(*obj_iter);
if (cur_score >= 0.f)
{
max_score = llmax(max_score, cur_score);
}
}
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)
{
F32 cur_score = calculate_score(*obj_iter);
if (cur_score >= 0.f)
{
max_score = llmax(max_score, cur_score);
}
}
score_map[iter->first] = max_score;
}
//set "score" for pending requests
for (std::unique_ptr<PendingRequestBase>& req_p : mPendingRequests)
{
req_p->setScore(score_map[req_p->getId()]);
req_p->checkScore();
}
//sort by "score"
@ -4602,7 +4582,9 @@ void LLMeshRepository::notifyLoadedMeshes()
}
while (!mPendingRequests.empty() && push_count > 0)
{
std::unique_ptr<PendingRequestBase>& req_p = mPendingRequests.front();
std::shared_ptr<PendingRequestBase>& req_p = mPendingRequests.front();
// todo: check hasTrackedData here and erase request if none
// since this is supposed to mean that request was removed
switch (req_p->getRequestType())
{
case MESH_REQUEST_LOD:
@ -4657,7 +4639,7 @@ void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo* info)
skin_load_map::iterator iter = mLoadingSkins.find(info->mMeshID);
if (iter != mLoadingSkins.end())
{
for (LLVOVolume* vobj : iter->second)
for (LLVOVolume* vobj : iter->second.mVolumes)
{
if (vobj)
{
@ -4673,7 +4655,7 @@ void LLMeshRepository::notifySkinInfoUnavailable(const LLUUID& mesh_id)
skin_load_map::iterator iter = mLoadingSkins.find(mesh_id);
if (iter != mLoadingSkins.end())
{
for (LLVOVolume* vobj : iter->second)
for (LLVOVolume* vobj : iter->second.mVolumes)
{
if (vobj)
{
@ -4737,7 +4719,7 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol
}
//notify waiting LLVOVolume instances that their requested mesh is available
for (LLVOVolume* vobj : obj_iter->second)
for (LLVOVolume* vobj : obj_iter->second.mVolumes)
{
if (vobj)
{
@ -4767,7 +4749,7 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params,
LLPrimitive::getVolumeManager()->unrefVolume(sys_volume);
}
for (LLVOVolume* vobj : obj_iter->second)
for (LLVOVolume* vobj : obj_iter->second.mVolumes)
{
if (vobj)
{
@ -4810,16 +4792,17 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, LLVOV
skin_load_map::iterator iter = mLoadingSkins.find(mesh_id);
if (iter != mLoadingSkins.end())
{ //request pending for this mesh, append volume id to list
auto it = std::find(iter->second.begin(), iter->second.end(), requesting_obj);
if (it == iter->second.end()) {
iter->second.push_back(requesting_obj);
auto it = std::find(iter->second.mVolumes.begin(), iter->second.mVolumes.end(), requesting_obj);
if (it == iter->second.mVolumes.end()) {
iter->second.addVolume(requesting_obj);
}
}
else
{
//first request for this mesh
mLoadingSkins[mesh_id].push_back(requesting_obj);
mPendingRequests.emplace_back(new PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN));
std::shared_ptr<PendingRequestBase> request(new PendingRequestUUID(mesh_id, MESH_REQUEST_SKIN));
mLoadingSkins[mesh_id].initData(requesting_obj, request);
mPendingRequests.emplace_back(request);
}
}
}

View File

@ -168,7 +168,6 @@ public:
void submitRequest(Request* request);
static S32 llcdCallback(const char*, S32, S32);
void cancel();
void setMeshData(LLCDMeshData& mesh, bool vertex_based);
void doDecomposition();
@ -206,19 +205,19 @@ private:
LLFrameTimer mTimer;
};
class MeshLoadData;
class PendingRequestBase
{
public:
struct CompareScoreGreater
{
bool operator()(const std::unique_ptr<PendingRequestBase>& lhs, const std::unique_ptr<PendingRequestBase>& rhs)
bool operator()(const std::shared_ptr<PendingRequestBase>& lhs, const std::shared_ptr<PendingRequestBase>& rhs)
{
return lhs->mScore > rhs->mScore; // greatest = first
}
};
PendingRequestBase() : mScore(0.f) {};
PendingRequestBase() : mScore(0.f), mTrackedData(nullptr), mScoreDirty(true) {};
virtual ~PendingRequestBase() {}
bool operator<(const PendingRequestBase& rhs) const
@ -226,14 +225,34 @@ public:
return mId < rhs.mId;
}
void setScore(F32 score) { mScore = score; }
F32 getScore() const { return mScore; }
void checkScore()
{
constexpr F32 EXPIRE_TIME_SECS = 8.f;
if (mScoreTimer.getElapsedTimeF32() > EXPIRE_TIME_SECS || mScoreDirty)
{
updateScore();
mScoreDirty = false;
mScoreTimer.reset();
}
};
LLUUID getId() const { return mId; }
virtual EMeshRequestType getRequestType() const = 0;
void trackData(MeshLoadData* data) { mTrackedData = data; mScoreDirty = true; }
void untrackData() { mTrackedData = nullptr; }
bool hasTrackedData() { return mTrackedData != nullptr; }
void setScoreDirty() { mScoreDirty = true; }
protected:
F32 mScore;
void updateScore();
LLUUID mId;
F32 mScore;
bool mScoreDirty;
LLTimer mScoreTimer;
MeshLoadData* mTrackedData;
};
class PendingRequestLOD : public PendingRequestBase
@ -267,6 +286,37 @@ private:
EMeshRequestType mRequestType;
};
class MeshLoadData
{
public:
MeshLoadData() {}
~MeshLoadData()
{
if (std::shared_ptr<PendingRequestBase> request = mRequest.lock())
{
request->untrackData();
}
}
void initData(LLVOVolume* vol, std::shared_ptr<PendingRequestBase>& request)
{
mVolumes.push_back(vol);
request->trackData(this);
mRequest = request;
}
void addVolume(LLVOVolume* vol)
{
mVolumes.push_back(vol);
if (std::shared_ptr<PendingRequestBase> request = mRequest.lock())
{
request->setScoreDirty();
}
}
std::vector<LLVOVolume*> mVolumes;
private:
std::weak_ptr<PendingRequestBase> mRequest;
};
class LLMeshHeader
{
public:
@ -814,7 +864,7 @@ public:
static void metricsProgress(unsigned int count);
static void metricsUpdate();
typedef std::unordered_map<LLUUID, std::vector<LLVOVolume*> > mesh_load_map;
typedef std::unordered_map<LLUUID, MeshLoadData> mesh_load_map;
mesh_load_map mLoadingMeshes[4];
typedef std::unordered_map<LLUUID, LLPointer<LLMeshSkinInfo>> skin_map;
@ -825,11 +875,11 @@ public:
LLMutex* mMeshMutex;
typedef std::vector <std::unique_ptr<PendingRequestBase> > pending_requests_vec;
typedef std::vector <std::shared_ptr<PendingRequestBase> > pending_requests_vec;
pending_requests_vec mPendingRequests;
//list of mesh ids awaiting skin info
typedef std::unordered_map<LLUUID, std::vector<LLVOVolume*> > skin_load_map;
typedef std::unordered_map<LLUUID, MeshLoadData > skin_load_map;
skin_load_map mLoadingSkins;
//list of mesh ids awaiting decompositions