#3725 Improve reporting of avatar statistics

1. Don't report UI avatars, they are local and UI specific
2. Split animeshes from normal avatars
3. Rename 'cloud' to 'missing parts', it's not related to 'cloud'
4. Exclude self
5. Report avatars held by meshes
meow-7.2.2
Andrey Kleshchev 2025-07-10 20:45:07 +03:00 committed by Andrey Kleshchev
parent 210abc3755
commit 2e931a55ad
7 changed files with 57 additions and 29 deletions

View File

@ -1094,12 +1094,12 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
{
gAgentAvatarp->setCompositeUpdatesEnabled(true);
// If we have not yet declouded, we may want to use
// If we have not yet loaded core parts, we may want to use
// baked texture UUIDs sent from the first objectUpdate message
// don't overwrite these. If we have already declouded, we've saved
// these ids as the last known good textures and can invalidate without
// re-clouding.
if (!gAgentAvatarp->getIsCloud())
// don't overwrite these. If we have parts already, we've saved
// these texture ids as the last known good textures and can
// invalidate without having to recloud avatar.
if (!gAgentAvatarp->getHasMissingParts())
{
gAgentAvatarp->invalidateAll();
}

View File

@ -856,7 +856,7 @@ void LLWearableHoldingPattern::checkMissingWearables()
// was requested but none was found, create a default asset as a replacement.
// In all other cases, don't do anything.
// For critical types (shape/hair/skin/eyes), this will keep the avatar as a cloud
// due to logic in LLVOAvatarSelf::getIsCloud().
// due to logic in LLVOAvatarSelf::getHasMissingParts().
// For non-critical types (tatoo, socks, etc.) the wearable will just be missing.
(requested_by_type[type] > 0) &&
((type == LLWearableType::WT_PANTS) || (type == LLWearableType::WT_SHIRT) || (type == LLWearableType::WT_SKIRT)))

View File

@ -678,6 +678,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mVisuallyMuteSetting(AV_RENDER_NORMALLY),
mMutedAVColor(LLColor4::white /* used for "uninitialize" */),
mFirstFullyVisible(true),
mWaitingForMeshes(false),
mFirstDecloudTime(-1.f),
mFullyLoaded(false),
mPreviousFullyLoaded(false),
@ -920,12 +921,12 @@ bool LLVOAvatar::isFullyTextured() const
bool LLVOAvatar::hasGray() const
{
return !getIsCloud() && !isFullyTextured();
return !getHasMissingParts() && !isFullyTextured();
}
S32 LLVOAvatar::getRezzedStatus() const
{
if (getIsCloud()) return 0;
if (getHasMissingParts()) return 0;
bool textured = isFullyTextured();
bool all_baked_loaded = allBakedTexturesCompletelyDownloaded();
if (textured && all_baked_loaded && getAttachmentCount() == mSimAttachments.size()) return 4;
@ -972,30 +973,45 @@ bool LLVOAvatar::areAllNearbyInstancesBaked(S32& grey_avatars)
}
// static
void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars)
void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars, S32& pending_meshes, S32& control_avatars)
{
counts.clear();
counts.resize(5);
avg_cloud_time = 0;
cloud_avatars = 0;
pending_meshes = 0;
control_avatars = 0;
S32 count_avg = 0;
for (LLCharacter* character : LLCharacter::sInstances)
{
if (LLVOAvatar* inst = (LLVOAvatar*)character)
LLVOAvatar* inst = (LLVOAvatar*)character;
if (inst && !inst->isUIAvatar() && !inst->isSelf())
{
S32 rez_status = inst->getRezzedStatus();
counts[rez_status]++;
F32 time = inst->getFirstDecloudTime();
if (time >= 0)
if (inst->isControlAvatar())
{
avg_cloud_time+=time;
count_avg++;
control_avatars++;
}
if (!inst->isFullyLoaded() || time < 0)
else
{
// still renders as cloud
cloud_avatars++;
S32 rez_status = inst->getRezzedStatus();
counts[rez_status]++;
F32 time = inst->getFirstDecloudTime();
if (time >= 0)
{
avg_cloud_time += time;
count_avg++;
}
if (!inst->isFullyLoaded() || time < 0)
{
// still renders as cloud
cloud_avatars++;
if (rez_status >= 4
&& inst->mWaitingForMeshes)
{
pending_meshes++;
}
}
}
}
}
@ -1012,7 +1028,7 @@ std::string LLVOAvatar::rezStatusToString(S32 rez_status)
switch (rez_status)
{
case 0:
return "cloud";
return "missing parts";
case 1:
return "gray";
case 2:
@ -3474,7 +3490,7 @@ void LLVOAvatar::idleUpdateNameTagText(bool new_name)
is_muted = isInMuteList();
}
bool is_friend = isBuddy();
bool is_cloud = getIsCloud();
bool is_cloud = getHasMissingParts();
if (is_appearance != mNameAppearance)
{
@ -8201,7 +8217,7 @@ bool LLVOAvatar::isVisible() const
}
// Determine if we have enough avatar data to render
bool LLVOAvatar::getIsCloud() const
bool LLVOAvatar::getHasMissingParts() const
{
if (mIsDummy)
{
@ -8408,8 +8424,12 @@ bool LLVOAvatar::updateIsFullyLoaded()
|| (mLoadedCallbackTextures < mCallbackTextureList.size() && mLastTexCallbackAddedTime.getElapsedTimeF32() < MAX_TEXTURE_WAIT_TIME_SEC)
|| !mPendingAttachment.empty()
|| (rez_status < 3 && !isFullyBaked())
|| hasPendingAttachedMeshes()
);
if (!loading)
{
mWaitingForMeshes = hasPendingAttachedMeshes();
loading = mWaitingForMeshes;
}
// compare amount of attachments to one reported by simulator
if (!isSelf() && mLastCloudAttachmentCount < mSimAttachments.size() && mSimAttachments.size() > 0)

View File

@ -400,7 +400,7 @@ public:
bool isTooComplex() const;
bool visualParamWeightsAreDefault();
virtual bool getIsCloud() const;
virtual bool getHasMissingParts() const;
bool isFullyTextured() const;
bool hasGray() const;
S32 getRezzedStatus() const; // 0 = cloud, 1 = gray, 2 = textured, 3 = textured and fully downloaded.
@ -427,6 +427,7 @@ protected:
private:
bool mFirstFullyVisible;
bool mWaitingForMeshes;
F32 mFirstDecloudTime;
LLFrameTimer mFirstAppearanceMessageTimer;
@ -723,7 +724,7 @@ public:
bool isFullyBaked();
static bool areAllNearbyInstancesBaked(S32& grey_avatars);
static void getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars);
static void getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars, S32& pending_meshes, S32& control_avatars);
static std::string rezStatusToString(S32 status);
//--------------------------------------------------------------------

View File

@ -1927,7 +1927,7 @@ void LLVOAvatarSelf::dumpTotalLocalTextureByteCount()
LL_INFOS() << "Total Avatar LocTex GL:" << (gl_bytes/1024) << "KB" << LL_ENDL;
}
bool LLVOAvatarSelf::getIsCloud() const
bool LLVOAvatarSelf::getHasMissingParts() const
{
// Let people know why they're clouded without spamming them into oblivion.
bool do_warn = false;
@ -2237,14 +2237,18 @@ void LLVOAvatarSelf::appearanceChangeMetricsCoro(std::string url)
std::vector<S32> rez_counts;
F32 avg_time;
S32 total_cloud_avatars;
LLVOAvatar::getNearbyRezzedStats(rez_counts, avg_time, total_cloud_avatars);
S32 waiting_for_meshes;
S32 control_avatars;
LLVOAvatar::getNearbyRezzedStats(rez_counts, avg_time, total_cloud_avatars, waiting_for_meshes, control_avatars);
for (S32 rez_stat = 0; rez_stat < rez_counts.size(); ++rez_stat)
{
std::string rez_status_name = LLVOAvatar::rezStatusToString(rez_stat);
msg["nearby"][rez_status_name] = rez_counts[rez_stat];
}
msg["nearby"]["waiting_for_meshes"] = waiting_for_meshes;
msg["nearby"]["avg_decloud_time"] = avg_time;
msg["nearby"]["cloud_total"] = total_cloud_avatars;
msg["nearby"]["animeshes"] = control_avatars;
// std::vector<std::string> bucket_fields("timer_name","is_self","grid_x","grid_y","is_using_server_bake");
std::vector<std::string> by_fields;

View File

@ -129,7 +129,7 @@ public:
// Loading state
//--------------------------------------------------------------------
public:
/*virtual*/ bool getIsCloud() const;
/*virtual*/ bool getHasMissingParts() const;
//--------------------------------------------------------------------
// Region state

View File

@ -43,12 +43,15 @@ namespace LLStatViewer
LLTrace::SampleStatHandle<> FPS_SAMPLE("fpssample");
}
void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars)
void LLVOAvatar::getNearbyRezzedStats(std::vector<S32>& counts, F32& avg_cloud_time, S32& cloud_avatars, S32& pending_meshes, S32& control_avatars)
{
counts.resize(3);
counts[0] = 0;
counts[1] = 0;
counts[2] = 1;
cloud_avatars = 0;
pending_meshes = 0;
control_avatars = 0;
}
// static