From b2a22e1a9bfe93d72503492f4d6096567e164937 Mon Sep 17 00:00:00 2001 From: Kitty Barnett Date: Thu, 29 Aug 2024 20:51:00 +0200 Subject: Resolve issues tracking worn attachments that aren't in COF yet (see PR for more details) --- indra/newview/llvoavatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llvoavatar.cpp') diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index efb09479e2..530fb71f6b 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -8588,7 +8588,7 @@ bool LLVOAvatar::processFullyLoadedChange(bool loading) bool LLVOAvatar::isFullyLoaded() const { - return (mRenderUnloadedAvatar || mFullyLoaded); + return (mRenderUnloadedAvatar && !isSelf()) || mFullyLoaded; } bool LLVOAvatar::hasFirstFullAttachmentData() const -- cgit v1.3 From 136eb7afcf22ea20344f22b1d6c46a6a207a360b Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Wed, 25 Mar 2026 23:59:45 +0200 Subject: #5579 Separate parts of calculateUpdateRenderComplexity Cleanup of the function, since these parts should remain unchanged. --- indra/newview/llvoavatar.cpp | 128 +++++++++++++++++++++++-------------------- indra/newview/llvoavatar.h | 4 +- 2 files changed, 73 insertions(+), 59 deletions(-) (limited to 'indra/newview/llvoavatar.cpp') diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 530fb71f6b..74fcae9849 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -11151,6 +11151,74 @@ void LLVOAvatar::updateVisualComplexity() } +U32 LLVOAvatar::calculateBodyPartsComplexity() +{ + static const U32 COMPLEXITY_BODY_PART_COST = 200; + U32 cost = 0; + for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++) + { + const LLAvatarAppearanceDictionary::BakedEntry* baked_dict + = LLAvatarAppearance::getDictionary()->getBakedTexture((EBakedTextureIndex)baked_index); + ETextureIndex tex_index = baked_dict->mTextureIndex; + if ((tex_index != TEX_SKIRT_BAKED) || (isWearingWearableType(LLWearableType::WT_SKIRT))) + { + // Same as isTextureVisible(), but doesn't account for isSelf to ensure identical numbers for all avatars + if (isIndexLocalTexture(tex_index)) + { + if (isTextureDefined(tex_index, 0)) + { + cost += COMPLEXITY_BODY_PART_COST; + } + } + else + { + // baked textures can use TE images directly + if (isTextureDefined(tex_index) + && (getTEImage(tex_index)->getID() != IMG_INVISIBLE || LLDrawPoolAlpha::sShowDebugAlpha)) + { + cost += COMPLEXITY_BODY_PART_COST; + } + } + } + } + LL_DEBUGS("ARCdetail") << "Avatar body parts complexity: " << cost << LL_ENDL; + return cost; +} + +void LLVOAvatar::processComplexityCostChange(hud_complexity_list_t hud_complexity_list, object_complexity_list_t object_complexity_list) +{ + static LLCachedControl show_my_complexity_changes(gSavedSettings, "ShowMyComplexityChanges", 20); + + if (isSelf() && show_my_complexity_changes) + { + // Avatar complexity + LLAvatarRenderNotifier::getInstance()->updateNotificationAgent(mVisualComplexity); + LLAvatarRenderNotifier::getInstance()->setObjectComplexityList(object_complexity_list); + // HUD complexity + LLHUDRenderNotifier::getInstance()->updateNotificationHUD(hud_complexity_list); + } + + //schedule an update to ART next frame if needed + if (LLPerfStats::tunables.userAutoTuneEnabled && + LLPerfStats::tunables.userFPSTuningStrategy != LLPerfStats::TUNE_SCENE_ONLY && + !isVisuallyMuted()) + { + const LLUUID id = getID(); // <== use id to make sure this avatar didn't get deleted between frames + LL::WorkQueue::getInstance("mainloop")->post([id]() + { + LLViewerObject* obj = gObjectList.findObject(id); + if (obj + && !obj->isDead() + && obj->isAvatar() + && obj->mDrawable) + { + LLVOAvatar* avatar = (LLVOAvatar*)obj; + gPipeline.profileAvatar(avatar); + } + }); + } +} + // Account for the complexity of a single top-level object associated // with an avatar. This will be either an attached object or an animated // object. @@ -11317,7 +11385,6 @@ void LLVOAvatar::calculateUpdateRenderComplexity() { LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; - static const U32 COMPLEXITY_BODY_PART_COST = 200; static LLCachedControl max_complexity_setting(gSavedSettings, "MaxAttachmentComplexity"); F32 max_attachment_complexity = max_complexity_setting; max_attachment_complexity = llmax(max_attachment_complexity, DEFAULT_MAX_ATTACHMENT_COMPLEXITY); @@ -11330,33 +11397,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity() hud_complexity_list_t hud_complexity_list; object_complexity_list_t object_complexity_list; - for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++) - { - const LLAvatarAppearanceDictionary::BakedEntry *baked_dict - = LLAvatarAppearance::getDictionary()->getBakedTexture((EBakedTextureIndex)baked_index); - ETextureIndex tex_index = baked_dict->mTextureIndex; - if ((tex_index != TEX_SKIRT_BAKED) || (isWearingWearableType(LLWearableType::WT_SKIRT))) - { - // Same as isTextureVisible(), but doesn't account for isSelf to ensure identical numbers for all avatars - if (isIndexLocalTexture(tex_index)) - { - if (isTextureDefined(tex_index, 0)) - { - cost += COMPLEXITY_BODY_PART_COST; - } - } - else - { - // baked textures can use TE images directly - if (isTextureDefined(tex_index) - && (getTEImage(tex_index)->getID() != IMG_INVISIBLE || LLDrawPoolAlpha::sShowDebugAlpha)) - { - cost += COMPLEXITY_BODY_PART_COST; - } - } - } - } - LL_DEBUGS("ARCdetail") << "Avatar body parts complexity: " << cost << LL_ENDL; + cost += calculateBodyPartsComplexity(); mAttachmentVisibleTriangleCount = 0; mAttachmentEstTriangleCount = 0.f; @@ -11409,36 +11450,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity() mVisualComplexity = cost; mVisualComplexityStale = false; - static LLCachedControl show_my_complexity_changes(gSavedSettings, "ShowMyComplexityChanges", 20); - - if (isSelf() && show_my_complexity_changes) - { - // Avatar complexity - LLAvatarRenderNotifier::getInstance()->updateNotificationAgent(mVisualComplexity); - LLAvatarRenderNotifier::getInstance()->setObjectComplexityList(object_complexity_list); - // HUD complexity - LLHUDRenderNotifier::getInstance()->updateNotificationHUD(hud_complexity_list); - } - - //schedule an update to ART next frame if needed - if (LLPerfStats::tunables.userAutoTuneEnabled && - LLPerfStats::tunables.userFPSTuningStrategy != LLPerfStats::TUNE_SCENE_ONLY && - !isVisuallyMuted()) - { - const LLUUID id = getID(); // <== use id to make sure this avatar didn't get deleted between frames - LL::WorkQueue::getInstance("mainloop")->post([id]() - { - LLViewerObject* obj = gObjectList.findObject(id); - if (obj - && !obj->isDead() - && obj->isAvatar() - && obj->mDrawable) - { - LLVOAvatar* avatar = (LLVOAvatar*)obj; - gPipeline.profileAvatar(avatar); - } - }); - } + processComplexityCostChange(hud_complexity_list, object_complexity_list); } } diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index fc3a97a25d..0e719bbd6f 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -594,6 +594,9 @@ private: VisualMuteSettings mVisuallyMuteSetting; // Always or never visually mute this AV + U32 calculateBodyPartsComplexity(); + + void processComplexityCostChange(hud_complexity_list_t hud_complexity_list, object_complexity_list_t object_complexity_list); //-------------------------------------------------------------------- // animated object status //-------------------------------------------------------------------- @@ -630,7 +633,6 @@ private: // Shadowing //-------------------------------------------------------------------- public: - void updateShadowFaces(); LLDrawable* mShadow; private: LLFace* mShadow0Facep; -- cgit v1.3 From c7576cddf3e548a7a6e63776da5674b7cb31297f Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Thu, 26 Mar 2026 00:01:18 +0200 Subject: #5579 Implement partial complexity calculations --- indra/newview/llvoavatar.cpp | 473 +++++++++++++++++++++++++++++++++++++++++-- indra/newview/llvoavatar.h | 95 ++++++++- indra/newview/llvovolume.cpp | 32 ++- 3 files changed, 580 insertions(+), 20 deletions(-) (limited to 'indra/newview/llvoavatar.cpp') diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 74fcae9849..c3bc51ad12 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -682,6 +682,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mUpdatePeriod(1), mOverallAppearance(AOA_INVISIBLE), mVisualComplexityStale(true), + mLastFullComplexityUpdate(0.0), mVisuallyMuteSetting(AV_RENDER_NORMALLY), mMutedAVColor(LLColor4::white /* used for "uninitialize" */), mFirstFullyVisible(true), @@ -7566,7 +7567,8 @@ const LLViewerJointAttachment *LLVOAvatar::attachObject(LLViewerObject *viewer_o updateAttachmentOverrides(); } - updateVisualComplexity(); + // Inform complexity logic to do partial update. + markAttachmentComplexityDirty(viewer_object->getID()); if (viewer_object->isSelected()) { @@ -7870,7 +7872,7 @@ bool LLVOAvatar::detachObject(LLViewerObject *viewer_object) if (attachment->isObjectAttached(viewer_object)) { - updateVisualComplexity(); + markAttachmentComplexityDirty(viewer_object->getID()); bool is_animated_object = viewer_object->isAnimatedObject(); cleanupAttachedMesh(viewer_object); @@ -8297,6 +8299,7 @@ void LLVOAvatar::updateRezzedStatusTimers(S32 rez_status) selfStopPhase("wear_inventory_category", false); selfStopPhase("process_initial_wearables_update", false); + // Start a complexity update. updateVisualComplexity(); } } @@ -9230,6 +9233,7 @@ void LLVOAvatar::releaseComponentTextures() { // Regression case of messaging system. Expected 21 textures, received 20. last texture is not valid so set to default setTETexture(TEX_HAIR_BAKED, IMG_DEFAULT_AVATAR); + markBodyPartsComplexityDirty(); } } @@ -9247,6 +9251,7 @@ void LLVOAvatar::releaseComponentTextures() { const U8 te = (ETextureIndex)bakedDicEntry->mLocalTextures[texture]; setTETexture(te, IMG_DEFAULT_AVATAR); + markBodyPartsComplexityDirty(); } } } @@ -10132,6 +10137,7 @@ void LLVOAvatar::onBakedTextureMasksLoaded( bool success, LLViewerFetchedTexture LL_INFOS() << "unexpected image id: " << id << LL_ENDL; } self->dirtyMesh(); + self->markBodyPartsComplexityDirty(); } else { @@ -10186,6 +10192,7 @@ void LLVOAvatar::onBakedTextureLoaded(bool success, if (selfp && !success) { selfp->removeMissingBakedTextures(); + selfp->markBodyPartsComplexityDirty(); } if( final || !success ) @@ -10196,6 +10203,7 @@ void LLVOAvatar::onBakedTextureLoaded(bool success, if( selfp && success && final ) { selfp->useBakedTexture( id ); + selfp->markBodyPartsComplexityDirty(); } } @@ -11146,10 +11154,432 @@ void LLVOAvatar::idleUpdateDebugInfo() void LLVOAvatar::updateVisualComplexity() { LL_DEBUGS("AvatarRender") << "avatar " << getID() << " appearance changed" << LL_ENDL; - // Set the cache time to in the past so it's updated ASAP + // Trigger cache recalculation on next idle update. + // Will recalculate stale data and control avatar. mVisualComplexityStale = true; } +// Constants for body part costs +static const F32 CACHE_LIFETIME_SECONDS = 30.0; // Todo: should be indefinite, until something actually changes + +void LLVOAvatar::calculateAttachmentComplexity(LLViewerObject* attached_object, + const F32 max_attachment_complexity, + ComplexityComponent& cache) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + + cache.reset(); + + if (!attached_object || attached_object->isDead()) + { + return; + } + + // Reuse the existing accountRenderComplexityForObject logic + // This ensures compatibility with the legacy implementation + hud_complexity_list_t attachment_hud_list; + object_complexity_list_t attachment_object_list; + cache.render_cost = 0; + cache.textures.clear(); + cache.triangle_count = 0; + cache.est_triangle_count = 0; + cache.surface_area = 0; + + accountRenderComplexityForObject( + attached_object, + max_attachment_complexity, + cache.textures, + cache.render_cost, + cache.triangle_count, + cache.est_triangle_count, + cache.surface_area, + attachment_hud_list, + attachment_object_list + ); + + // Store HUD/object complexity info for later reporting + if (!attachment_hud_list.empty()) + { + cache.hud_complexity = attachment_hud_list.front(); + } + if (!attachment_object_list.empty()) + { + cache.object_complexity = attachment_object_list.front(); + } + + cache.last_update_time = LLFrameTimer::getTotalSeconds(); + cache.needs_update = false; +} + +void LLVOAvatar::calculateBodyPartsComplexity(ComplexityComponent& cache) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + + cache.reset(); + + // Body parts have a fixed cost + // This represents the base avatar mesh (eyes, hair, shape, skin, etc.) + cache.render_cost = calculateBodyPartsComplexity(); + + // For more accurate body part complexity, could enumerate mesh LODs here + // For now, using a constant cost as in the original implementation + + cache.last_update_time = LLFrameTimer::getTotalSeconds(); + cache.needs_update = false; +} + +bool LLVOAvatar::shouldUpdateComplexityComponent(const ComplexityComponent& component) const +{ + if (component.needs_update) + { + return true; + } + + F64 current_time = LLFrameTimer::getTotalSeconds(); + return (current_time - component.last_update_time) > CACHE_LIFETIME_SECONDS; +} + +bool LLVOAvatar::calculateControlAvatarComplexity(ComplexityComponent& cache, const F32 max_attachment_complexity) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + + cache.reset(); + + // For control avatars (animated objects), we need to account for + // the skeleton and animation system overhead + if (!isControlAvatar()) + { + return false; + } + + LLControlAvatar* control_av = dynamic_cast(this); + if (!control_av) + { + return false; + } + + LLVOVolume* volp = control_av->mRootVolp; + if (!volp || volp->isAttachment()) + { + return false; + } + + hud_complexity_list_t hud_list; + object_complexity_list_t object_list; + cache.render_cost = 0; + cache.textures.clear(); + cache.triangle_count = 0; + cache.est_triangle_count = 0; + cache.surface_area = 0; + + accountRenderComplexityForObject( + volp, + max_attachment_complexity, + cache.textures, + cache.render_cost, + cache.triangle_count, + cache.est_triangle_count, + cache.surface_area, + hud_list, + object_list + ); + + // Store HUD/object complexity info for later reporting + if (!hud_list.empty()) + { + cache.hud_complexity = hud_list.front(); + } + if (!object_list.empty()) + { + cache.object_complexity = object_list.front(); + } + + cache.last_update_time = LLFrameTimer::getTotalSeconds(); + cache.needs_update = false; + + return true; +} + +void LLVOAvatar::accumulateComplexityComponent(const ComplexityComponent& component, + U32& total_cost, + LLVOVolume::texture_cost_t& all_textures, + hud_complexity_list_t& hud_list, + object_complexity_list_t& object_list) +{ + total_cost += component.render_cost; + mAttachmentSurfaceArea += component.surface_area; + mAttachmentVisibleTriangleCount += component.triangle_count; + mAttachmentEstTriangleCount += component.est_triangle_count; + + // Merge textures (avoid double-counting) + all_textures.insert(component.textures.begin(), component.textures.end()); + + // Add HUD/object complexity info if present + if (component.hud_complexity.objectId.notNull()) + { + hud_list.push_back(component.hud_complexity); + } + if (component.object_complexity.objectId.notNull()) + { + object_list.push_back(component.object_complexity); + } +} + +void LLVOAvatar::markAttachmentComplexityDirty(const LLUUID& object_id) +{ + mDirtyComplexityAttachments.insert(object_id); + + // Also mark the cache entry if it exists + complexity_cache_map_t::iterator it = mComplexityCache.find(object_id); + if (it != mComplexityCache.end()) + { + it->second.needs_update = true; + } + + // Launch update process if not already scheduled + mVisualComplexityStale = true; +} + +void LLVOAvatar::markAllComplexityDirty() +{ + for (complexity_cache_map_t::iterator it = mComplexityCache.begin(); + it != mComplexityCache.end(); ++it) + { + it->second.needs_update = true; + mDirtyComplexityAttachments.insert(it->first); + } + mBodyPartsComplexity.needs_update = true; + mControlAvatarComplexity.needs_update = true; + + // Launch update process if not already scheduled + mVisualComplexityStale = true; +} + +void LLVOAvatar::markBodyPartsComplexityDirty() +{ + mBodyPartsComplexity.needs_update = true; + + // Launch update process if not already scheduled + mVisualComplexityStale = true; +} + +void LLVOAvatar::updateDirtyAttachmentComplexity(const F32 max_attachment_complexity) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + + if (mDirtyComplexityAttachments.empty()) + { + return; + } + + // Update only attachments marked as dirty + for (std::set::iterator dirty_it = mDirtyComplexityAttachments.begin(); + dirty_it != mDirtyComplexityAttachments.end(); ++dirty_it) + { + const LLUUID& object_id = *dirty_it; + + // Find the actual attachment object + LLViewerObject* attached_obj = gObjectList.findObject(object_id); + if (attached_obj && !attached_obj->isDead() && attached_obj->getAttachmentItemID().notNull()) + { + ComplexityComponent& cache = mComplexityCache[object_id]; + calculateAttachmentComplexity(attached_obj, max_attachment_complexity, cache); + } + else + { + // Object no longer exists, remove from cache + mComplexityCache.erase(object_id); + } + } + + mDirtyComplexityAttachments.clear(); +} + +void LLVOAvatar::performFullComplexityUpdate(const F32 max_attachment_complexity) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + + // Clear the cache and recalculate everything + mComplexityCache.clear(); + mDirtyComplexityAttachments.clear(); + + // Recalculate all attachments + for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); + iter != mAttachmentPoints.end(); ++iter) + { + LLViewerJointAttachment* attachment = iter->second; + if (!attachment || !attachment->getValid()) + { + continue; + } + + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) + { + LLViewerObject* attached_object = attachment_iter->get(); + if (attached_object && !attached_object->isDead()) + { + LLUUID object_id = attached_object->getID(); + ComplexityComponent& cache = mComplexityCache[object_id]; + calculateAttachmentComplexity(attached_object, max_attachment_complexity, cache); + } + } + } + + // Recalculate body parts + calculateBodyPartsComplexity(mBodyPartsComplexity); + + mLastFullComplexityUpdate = LLFrameTimer::getTotalSeconds(); +} + +void LLVOAvatar::performPartialComplexityUpdate(const F32 max_attachment_complexity) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + + // Update any attachments marked as dirty + updateDirtyAttachmentComplexity(max_attachment_complexity); + + // Update stale cache entries + for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); + iter != mAttachmentPoints.end(); ++iter) + { + LLViewerJointAttachment* attachment = iter->second; + if (!attachment || !attachment->getValid()) + { + continue; + } + + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) + { + LLViewerObject* attached_object = attachment_iter->get(); + if (attached_object && !attached_object->isDead()) + { + LLUUID object_id = attached_object->getID(); + ComplexityComponent& cache = mComplexityCache[object_id]; + + // Update if cache is stale + if (shouldUpdateComplexityComponent(cache)) + { + calculateAttachmentComplexity(attached_object, max_attachment_complexity, cache); + } + } + } + } + + // Update body parts if stale + if (shouldUpdateComplexityComponent(mBodyPartsComplexity)) + { + calculateBodyPartsComplexity(mBodyPartsComplexity); + } +} + +// Calculations for mVisualComplexity value +void LLVOAvatar::calculateUpdateRenderComplexity() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; + + // **************************************************************** + // This calculation should not be modified by third party viewers, + // since it is used to limit rendering and should be uniform for + // everyone. If you have suggested improvements, submit them to + // the official viewer for consideration. + // **************************************************************** + + if (!mVisualComplexityStale) + { + return; + } + + // Get the attachment complexity limit + static LLCachedControl max_complexity_setting(gSavedSettings, "MaxAttachmentComplexity"); + F32 max_attachment_complexity = max_complexity_setting; + max_attachment_complexity = llmax(max_attachment_complexity, DEFAULT_MAX_ATTACHMENT_COMPLEXITY); + + // Reset per-run counters + mAttachmentSurfaceArea = 0.f; + mAttachmentVisibleTriangleCount = 0; + mAttachmentEstTriangleCount = 0.f; + + U32 total_cost = 0; + LLVOVolume::texture_cost_t all_textures; // Not currently in use. Perhaps add cost from unique textures? + hud_complexity_list_t hud_complexity_list; + object_complexity_list_t object_complexity_list; + + // Determine update strategy + F64 current_time = LLFrameTimer::getTotalSeconds(); + + // Todo: check if it changes anything, then get rid of FULL_UPDATE_INTERVAL, + // full recalculation can take over 10ms for very complex avatars, + // thus is not practical to do. Leave only partial updates. + constexpr F64 FULL_UPDATE_INTERVAL = 60.0; // seconds + bool do_full_update = (current_time - mLastFullComplexityUpdate) > FULL_UPDATE_INTERVAL; + + // Perform update based on strategy + if (do_full_update) + { + // Expensive! Todo: don't do. + performFullComplexityUpdate(max_attachment_complexity); + } + else + { + performPartialComplexityUpdate(max_attachment_complexity); + } + + // Calculate and accumulate control avatar complexity if applicable + // For now this is on each run. + // Todo: See if mControlAvatarComplexity.needs_update is applicable here. + if (calculateControlAvatarComplexity(mControlAvatarComplexity, max_attachment_complexity)) + { + accumulateComplexityComponent( + mControlAvatarComplexity, + total_cost, + all_textures, + hud_complexity_list, + object_complexity_list); + } + + // Accumulate body parts complexity + accumulateComplexityComponent(mBodyPartsComplexity, total_cost, + all_textures, hud_complexity_list, object_complexity_list); + + // Accumulate all attachment complexity from cache + // Clean up cache entries for attachments that no longer exist + std::vector to_remove; + + for (complexity_cache_map_t::iterator cache_iter = mComplexityCache.begin(); + cache_iter != mComplexityCache.end(); ++cache_iter) + { + const LLUUID& object_id = cache_iter->first; + + // Verify object still exists + LLViewerObject* obj = gObjectList.findObject(object_id); + if (!obj || obj->isDead()) + { + to_remove.push_back(object_id); + continue; + } + + // Accumulate this attachment's complexity + accumulateComplexityComponent(cache_iter->second, total_cost, + all_textures, hud_complexity_list, object_complexity_list); + } + + // Remove stale cache entries + for (std::vector::iterator it = to_remove.begin(); it != to_remove.end(); ++it) + { + mComplexityCache.erase(*it); + } + + // Store results + mVisualComplexity = total_cost; + + // Call the existing reporting function with the aggregated lists + processComplexityCostChange(hud_complexity_list, object_complexity_list); + + // Stop processing until something changes + mVisualComplexityStale = false; +} U32 LLVOAvatar::calculateBodyPartsComplexity() { @@ -11227,15 +11657,18 @@ void LLVOAvatar::accountRenderComplexityForObject( const F32 max_attachment_complexity, LLVOVolume::texture_cost_t& textures, U32& cost, + U32& visible_triangle_count, + F32& est_triangle_count, + F32& surface_area, hud_complexity_list_t& hud_complexity_list, object_complexity_list_t& object_complexity_list) { LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; if (attached_object && !attached_object->isHUDAttachment()) { - mAttachmentVisibleTriangleCount += attached_object->recursiveGetTriangleCount(); - mAttachmentEstTriangleCount += attached_object->recursiveGetEstTrianglesMax(); - mAttachmentSurfaceArea += attached_object->recursiveGetScaledSurfaceArea(); + visible_triangle_count += attached_object->recursiveGetTriangleCount(); + est_triangle_count += attached_object->recursiveGetEstTrianglesMax(); + surface_area += attached_object->recursiveGetScaledSurfaceArea(); textures.clear(); const LLDrawable* drawable = attached_object->mDrawable; @@ -11306,7 +11739,7 @@ void LLVOAvatar::accountRenderComplexityForObject( && attached_object->mDrawable) { textures.clear(); - mAttachmentSurfaceArea += attached_object->recursiveGetScaledSurfaceArea(); + surface_area += attached_object->recursiveGetScaledSurfaceArea(); const LLVOVolume* volume = attached_object->mDrawable->getVOVolume(); if (volume) @@ -11373,7 +11806,7 @@ void LLVOAvatar::accountRenderComplexityForObject( } // Calculations for mVisualComplexity value -void LLVOAvatar::calculateUpdateRenderComplexity() +void LLVOAvatar::calculateUpdateRenderComplexityLegacy() { /***************************************************************** * This calculation should not be modified by third party viewers, @@ -11412,8 +11845,16 @@ void LLVOAvatar::calculateUpdateRenderComplexity() LLVOVolume *volp = control_av->mRootVolp; if (volp && !volp->isAttachment()) { - accountRenderComplexityForObject(volp, max_attachment_complexity, - textures, cost, hud_complexity_list, object_complexity_list); + accountRenderComplexityForObject( + volp, + max_attachment_complexity, + textures, + cost, + mAttachmentVisibleTriangleCount, + mAttachmentEstTriangleCount, + mAttachmentSurfaceArea, + hud_complexity_list, + object_complexity_list); } } @@ -11428,8 +11869,16 @@ void LLVOAvatar::calculateUpdateRenderComplexity() ++attachment_iter) { LLViewerObject* attached_object = attachment_iter->get(); - accountRenderComplexityForObject(attached_object, max_attachment_complexity, - textures, cost, hud_complexity_list, object_complexity_list); + accountRenderComplexityForObject( + attached_object, + max_attachment_complexity, + textures, + cost, + mAttachmentVisibleTriangleCount, + mAttachmentEstTriangleCount, + mAttachmentSurfaceArea, + hud_complexity_list, + object_complexity_list); } } diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 0e719bbd6f..79a03527fb 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -302,11 +302,19 @@ public: const F32 max_attachment_complexity, LLVOVolume::texture_cost_t& textures, U32& cost, + U32& visible_triangle_count, + F32& est_triangle_count, + F32& surface_area, hud_complexity_list_t& hud_complexity_list, object_complexity_list_t& object_complexity_list); void calculateUpdateRenderComplexity(); + void calculateUpdateRenderComplexityLegacy(); static const U32 VISUAL_COMPLEXITY_UNKNOWN; void updateVisualComplexity(); + // Mark that an attachment needs complexity recalculation + void markAttachmentComplexityDirty(const LLUUID& object_id); + void markAllComplexityDirty(); + void markBodyPartsComplexityDirty(); void placeProfileQuery(); void readProfileQuery(S32 retries); @@ -581,12 +589,6 @@ private: // CPU render time in ms F32 mCPURenderTime = 0.f; - // the isTooComplex method uses these mutable values to avoid recalculating too frequently - // DEPRECATED -- obsolete avatar render cost values - mutable U32 mVisualComplexity; - mutable bool mVisualComplexityStale; - U32 mReportedVisualComplexity; // from other viewers through the simulator - mutable bool mCachedInMuteList; mutable F64 mCachedMuteListUpdateTime; mutable bool mCachedInBuddyList = false; @@ -594,9 +596,90 @@ private: VisualMuteSettings mVisuallyMuteSetting; // Always or never visually mute this AV + //-------------------------------------------------------------------- + // Complexity calculation and caching + //-------------------------------------------------------------------- + +private: + // Structure to cache complexity metrics for individual attachments or components + struct ComplexityComponent + { + U32 render_cost; + U32 triangle_count; + F32 surface_area; + F32 est_triangle_count; + LLVOVolume::texture_cost_t textures; + F64 last_update_time; + bool needs_update; + + // For tracking HUD and object lists + LLHUDComplexity hud_complexity; + LLObjectComplexity object_complexity; + + ComplexityComponent() + : render_cost(0) + , triangle_count(0) + , surface_area(0.f) + , est_triangle_count(0.f) + , last_update_time(0.0) + , needs_update(true) + { + } + + void reset() + { + render_cost = 0; + triangle_count = 0; + surface_area = 0.f; + est_triangle_count = 0.f; + textures.clear(); + hud_complexity.reset(); + object_complexity.reset(); + needs_update = true; + } + }; + + void calculateAttachmentComplexity(LLViewerObject* attached_object, + const F32 max_attachment_complexity, + ComplexityComponent& cache); + void calculateBodyPartsComplexity(ComplexityComponent& cache); U32 calculateBodyPartsComplexity(); + // return true, if a valid control avatar. + bool calculateControlAvatarComplexity(ComplexityComponent& cache, const F32 max_attachment_complexity); + + void accumulateComplexityComponent(const ComplexityComponent& component, + U32& total_cost, + LLVOVolume::texture_cost_t& all_textures, + hud_complexity_list_t& hud_list, + object_complexity_list_t& object_list); + + bool shouldUpdateComplexityComponent(const ComplexityComponent& component) const; + void updateDirtyAttachmentComplexity(const F32 max_attachment_complexity); + void performFullComplexityUpdate(const F32 max_attachment_complexity); + void performPartialComplexityUpdate(const F32 max_attachment_complexity); + void processComplexityCostChange(hud_complexity_list_t hud_complexity_list, object_complexity_list_t object_complexity_list); + + // Todo: probably safe to store by local instead of global id + // since they should be unique to this avatar, but local id might be not known. + typedef std::map complexity_cache_map_t; + complexity_cache_map_t mComplexityCache; // Cache per-attachment complexity + ComplexityComponent mBodyPartsComplexity; // Cache for body parts (mesh, eyes, hair, etc) + ComplexityComponent mControlAvatarComplexity; // Cache for animated object control avatar + F64 mLastFullComplexityUpdate; + + // Attachments marked for update, + // Todo: probably safe to store by local instead of global id + // since they should be unique to this avatar, but local id might be not known. + std::set mDirtyComplexityAttachments; + + // the isTooComplex method uses these mutable values to avoid recalculating too frequently + // DEPRECATED -- obsolete avatar render cost values + mutable U32 mVisualComplexity; + mutable bool mVisualComplexityStale; + U32 mReportedVisualComplexity; // from other viewers through the simulator + //-------------------------------------------------------------------- // animated object status //-------------------------------------------------------------------- diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index c12f65babf..1cb6d2ecc5 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1253,7 +1253,35 @@ void LLVOVolume::updateSculptTexture() void LLVOVolume::updateVisualComplexity() { - LLVOAvatar* avatar = getAvatarAncestor(); + LLVOAvatar* avatar = nullptr; + LLViewerObject* pobj = (LLViewerObject*)getParent(); + LLViewerObject* lobj = this; + while (pobj) + { + avatar = pobj->asAvatar(); + if (avatar) + { + break; + } + lobj = pobj; + pobj = (LLViewerObject*)pobj->getParent(); + } + + if (avatar) + { + // mark parent as dirty, complexity will be updated recursively. + avatar->markAttachmentComplexityDirty(lobj->getID()); + } + LLVOAvatar* rigged_avatar = getAvatar(); + if (rigged_avatar && (rigged_avatar != avatar)) + { + // This might be wrong. Control avatars update each run, + // due to lack of dirty mechanics and this might be + // where we should implement and call + // markCotrolAvatarComplexityDirty() if !isAttachment(). + rigged_avatar->markAttachmentComplexityDirty(lobj->getID()); + } + /*LLVOAvatar* avatar = getAvatarAncestor(); if (avatar) { avatar->updateVisualComplexity(); @@ -1262,7 +1290,7 @@ void LLVOVolume::updateVisualComplexity() if(rigged_avatar && (rigged_avatar != avatar)) { rigged_avatar->updateVisualComplexity(); - } + }*/ } void LLVOVolume::notifyMeshLoaded() -- cgit v1.3 From e2d8ace323fa4980a6a6e12f4585c2b90f388934 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Fri, 27 Mar 2026 01:06:27 +0200 Subject: #5579 Cleanup --- indra/newview/llvoavatar.cpp | 337 ++++++++----------------------------------- indra/newview/llvoavatar.h | 19 +-- indra/newview/llvovolume.cpp | 4 +- 3 files changed, 65 insertions(+), 295 deletions(-) (limited to 'indra/newview/llvoavatar.cpp') diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index c3bc51ad12..bb152ced40 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -682,7 +682,6 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mUpdatePeriod(1), mOverallAppearance(AOA_INVISIBLE), mVisualComplexityStale(true), - mLastFullComplexityUpdate(0.0), mVisuallyMuteSetting(AV_RENDER_NORMALLY), mMutedAVColor(LLColor4::white /* used for "uninitialize" */), mFirstFullyVisible(true), @@ -7872,7 +7871,7 @@ bool LLVOAvatar::detachObject(LLViewerObject *viewer_object) if (attachment->isObjectAttached(viewer_object)) { - markAttachmentComplexityDirty(viewer_object->getID()); + markAttachmentComplexityDirty(viewer_object->getID(), true); bool is_animated_object = viewer_object->isAnimatedObject(); cleanupAttachedMesh(viewer_object); @@ -11155,13 +11154,10 @@ void LLVOAvatar::updateVisualComplexity() { LL_DEBUGS("AvatarRender") << "avatar " << getID() << " appearance changed" << LL_ENDL; // Trigger cache recalculation on next idle update. - // Will recalculate stale data and control avatar. + // Will recalculate stale, missing data and control avatar. mVisualComplexityStale = true; } -// Constants for body part costs -static const F32 CACHE_LIFETIME_SECONDS = 30.0; // Todo: should be indefinite, until something actually changes - void LLVOAvatar::calculateAttachmentComplexity(LLViewerObject* attached_object, const F32 max_attachment_complexity, ComplexityComponent& cache) @@ -11170,21 +11166,12 @@ void LLVOAvatar::calculateAttachmentComplexity(LLViewerObject* attached_object, cache.reset(); - if (!attached_object || attached_object->isDead()) + if (!attached_object + || attached_object->isDead()) { return; } - // Reuse the existing accountRenderComplexityForObject logic - // This ensures compatibility with the legacy implementation - hud_complexity_list_t attachment_hud_list; - object_complexity_list_t attachment_object_list; - cache.render_cost = 0; - cache.textures.clear(); - cache.triangle_count = 0; - cache.est_triangle_count = 0; - cache.surface_area = 0; - accountRenderComplexityForObject( attached_object, max_attachment_complexity, @@ -11193,20 +11180,10 @@ void LLVOAvatar::calculateAttachmentComplexity(LLViewerObject* attached_object, cache.triangle_count, cache.est_triangle_count, cache.surface_area, - attachment_hud_list, - attachment_object_list + cache.hud_complexity, + cache.object_complexity ); - // Store HUD/object complexity info for later reporting - if (!attachment_hud_list.empty()) - { - cache.hud_complexity = attachment_hud_list.front(); - } - if (!attachment_object_list.empty()) - { - cache.object_complexity = attachment_object_list.front(); - } - cache.last_update_time = LLFrameTimer::getTotalSeconds(); cache.needs_update = false; } @@ -11235,6 +11212,7 @@ bool LLVOAvatar::shouldUpdateComplexityComponent(const ComplexityComponent& comp return true; } + constexpr F32 CACHE_LIFETIME_SECONDS = 30.0; // Todo: should be indefinite, until something actually changes F64 current_time = LLFrameTimer::getTotalSeconds(); return (current_time - component.last_update_time) > CACHE_LIFETIME_SECONDS; } @@ -11245,8 +11223,6 @@ bool LLVOAvatar::calculateControlAvatarComplexity(ComplexityComponent& cache, co cache.reset(); - // For control avatars (animated objects), we need to account for - // the skeleton and animation system overhead if (!isControlAvatar()) { return false; @@ -11264,14 +11240,6 @@ bool LLVOAvatar::calculateControlAvatarComplexity(ComplexityComponent& cache, co return false; } - hud_complexity_list_t hud_list; - object_complexity_list_t object_list; - cache.render_cost = 0; - cache.textures.clear(); - cache.triangle_count = 0; - cache.est_triangle_count = 0; - cache.surface_area = 0; - accountRenderComplexityForObject( volp, max_attachment_complexity, @@ -11280,20 +11248,11 @@ bool LLVOAvatar::calculateControlAvatarComplexity(ComplexityComponent& cache, co cache.triangle_count, cache.est_triangle_count, cache.surface_area, - hud_list, - object_list + cache.hud_complexity, + cache.object_complexity ); - // Store HUD/object complexity info for later reporting - if (!hud_list.empty()) - { - cache.hud_complexity = hud_list.front(); - } - if (!object_list.empty()) - { - cache.object_complexity = object_list.front(); - } - + // todo: store 'expires' time instead or make it indefinite? cache.last_update_time = LLFrameTimer::getTotalSeconds(); cache.needs_update = false; @@ -11302,7 +11261,6 @@ bool LLVOAvatar::calculateControlAvatarComplexity(ComplexityComponent& cache, co void LLVOAvatar::accumulateComplexityComponent(const ComplexityComponent& component, U32& total_cost, - LLVOVolume::texture_cost_t& all_textures, hud_complexity_list_t& hud_list, object_complexity_list_t& object_list) { @@ -11311,9 +11269,6 @@ void LLVOAvatar::accumulateComplexityComponent(const ComplexityComponent& compon mAttachmentVisibleTriangleCount += component.triangle_count; mAttachmentEstTriangleCount += component.est_triangle_count; - // Merge textures (avoid double-counting) - all_textures.insert(component.textures.begin(), component.textures.end()); - // Add HUD/object complexity info if present if (component.hud_complexity.objectId.notNull()) { @@ -11325,33 +11280,23 @@ void LLVOAvatar::accumulateComplexityComponent(const ComplexityComponent& compon } } -void LLVOAvatar::markAttachmentComplexityDirty(const LLUUID& object_id) +void LLVOAvatar::markAttachmentComplexityDirty(const LLUUID& object_id, bool force_reset_attachment) { - mDirtyComplexityAttachments.insert(object_id); - - // Also mark the cache entry if it exists + // Mark the cache entry if it exists complexity_cache_map_t::iterator it = mComplexityCache.find(object_id); if (it != mComplexityCache.end()) { + if (force_reset_attachment) + { + // Object was detached. + // Force reset it in case it lingers in gObjectList for some reason (ex: dropped to world). + it->second.reset(); + } it->second.needs_update = true; } // Launch update process if not already scheduled - mVisualComplexityStale = true; -} - -void LLVOAvatar::markAllComplexityDirty() -{ - for (complexity_cache_map_t::iterator it = mComplexityCache.begin(); - it != mComplexityCache.end(); ++it) - { - it->second.needs_update = true; - mDirtyComplexityAttachments.insert(it->first); - } - mBodyPartsComplexity.needs_update = true; - mControlAvatarComplexity.needs_update = true; - - // Launch update process if not already scheduled + // It will add any missing attachments. mVisualComplexityStale = true; } @@ -11363,83 +11308,15 @@ void LLVOAvatar::markBodyPartsComplexityDirty() mVisualComplexityStale = true; } -void LLVOAvatar::updateDirtyAttachmentComplexity(const F32 max_attachment_complexity) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; - - if (mDirtyComplexityAttachments.empty()) - { - return; - } - - // Update only attachments marked as dirty - for (std::set::iterator dirty_it = mDirtyComplexityAttachments.begin(); - dirty_it != mDirtyComplexityAttachments.end(); ++dirty_it) - { - const LLUUID& object_id = *dirty_it; - - // Find the actual attachment object - LLViewerObject* attached_obj = gObjectList.findObject(object_id); - if (attached_obj && !attached_obj->isDead() && attached_obj->getAttachmentItemID().notNull()) - { - ComplexityComponent& cache = mComplexityCache[object_id]; - calculateAttachmentComplexity(attached_obj, max_attachment_complexity, cache); - } - else - { - // Object no longer exists, remove from cache - mComplexityCache.erase(object_id); - } - } - - mDirtyComplexityAttachments.clear(); -} - -void LLVOAvatar::performFullComplexityUpdate(const F32 max_attachment_complexity) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; - - // Clear the cache and recalculate everything - mComplexityCache.clear(); - mDirtyComplexityAttachments.clear(); - - // Recalculate all attachments - for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); - iter != mAttachmentPoints.end(); ++iter) - { - LLViewerJointAttachment* attachment = iter->second; - if (!attachment || !attachment->getValid()) - { - continue; - } - - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); ++attachment_iter) - { - LLViewerObject* attached_object = attachment_iter->get(); - if (attached_object && !attached_object->isDead()) - { - LLUUID object_id = attached_object->getID(); - ComplexityComponent& cache = mComplexityCache[object_id]; - calculateAttachmentComplexity(attached_object, max_attachment_complexity, cache); - } - } - } - - // Recalculate body parts - calculateBodyPartsComplexity(mBodyPartsComplexity); - - mLastFullComplexityUpdate = LLFrameTimer::getTotalSeconds(); -} - void LLVOAvatar::performPartialComplexityUpdate(const F32 max_attachment_complexity) { LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; // Update any attachments marked as dirty - updateDirtyAttachmentComplexity(max_attachment_complexity); + // Todo: might want to limit time or count here and defer the rest + // till next run. In such a case will need to make sure + // mVisualComplexityStale remains true. - // Update stale cache entries for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ++iter) { @@ -11458,7 +11335,7 @@ void LLVOAvatar::performPartialComplexityUpdate(const F32 max_attachment_complex LLUUID object_id = attached_object->getID(); ComplexityComponent& cache = mComplexityCache[object_id]; - // Update if cache is stale + // Update if cache is stale or a new entry. if (shouldUpdateComplexityComponent(cache)) { calculateAttachmentComplexity(attached_object, max_attachment_complexity, cache); @@ -11475,6 +11352,8 @@ void LLVOAvatar::performPartialComplexityUpdate(const F32 max_attachment_complex } // Calculations for mVisualComplexity value +// Call rate is flexible, can be once in 20, can be once in 200 frames, +// depends on priority and known cost of an avatar in question. void LLVOAvatar::calculateUpdateRenderComplexity() { LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; @@ -11496,36 +11375,25 @@ void LLVOAvatar::calculateUpdateRenderComplexity() F32 max_attachment_complexity = max_complexity_setting; max_attachment_complexity = llmax(max_attachment_complexity, DEFAULT_MAX_ATTACHMENT_COMPLEXITY); + // Update complexity for any dirty attachments or body parts. + // + // Todo: Limit this by time or count and continue later as + // doing everything in one go can be very expensive (multiple ms) + // Note that calculateUpdateRenderComplexity() can be launched once + // per 200 frames. Limiting it by time or count runs the risk of + // already checked attachments getting stale on last_update_time, + // thus function will keep running indefinetely. + performPartialComplexityUpdate(max_attachment_complexity); + // Reset per-run counters mAttachmentSurfaceArea = 0.f; mAttachmentVisibleTriangleCount = 0; mAttachmentEstTriangleCount = 0.f; U32 total_cost = 0; - LLVOVolume::texture_cost_t all_textures; // Not currently in use. Perhaps add cost from unique textures? hud_complexity_list_t hud_complexity_list; object_complexity_list_t object_complexity_list; - // Determine update strategy - F64 current_time = LLFrameTimer::getTotalSeconds(); - - // Todo: check if it changes anything, then get rid of FULL_UPDATE_INTERVAL, - // full recalculation can take over 10ms for very complex avatars, - // thus is not practical to do. Leave only partial updates. - constexpr F64 FULL_UPDATE_INTERVAL = 60.0; // seconds - bool do_full_update = (current_time - mLastFullComplexityUpdate) > FULL_UPDATE_INTERVAL; - - // Perform update based on strategy - if (do_full_update) - { - // Expensive! Todo: don't do. - performFullComplexityUpdate(max_attachment_complexity); - } - else - { - performPartialComplexityUpdate(max_attachment_complexity); - } - // Calculate and accumulate control avatar complexity if applicable // For now this is on each run. // Todo: See if mControlAvatarComplexity.needs_update is applicable here. @@ -11534,14 +11402,12 @@ void LLVOAvatar::calculateUpdateRenderComplexity() accumulateComplexityComponent( mControlAvatarComplexity, total_cost, - all_textures, hud_complexity_list, object_complexity_list); } // Accumulate body parts complexity - accumulateComplexityComponent(mBodyPartsComplexity, total_cost, - all_textures, hud_complexity_list, object_complexity_list); + accumulateComplexityComponent(mBodyPartsComplexity, total_cost, hud_complexity_list, object_complexity_list); // Accumulate all attachment complexity from cache // Clean up cache entries for attachments that no longer exist @@ -11554,7 +11420,9 @@ void LLVOAvatar::calculateUpdateRenderComplexity() // Verify object still exists LLViewerObject* obj = gObjectList.findObject(object_id); - if (!obj || obj->isDead()) + if (!obj + || obj->isDead() + || !obj->isAttachment()) { to_remove.push_back(object_id); continue; @@ -11562,7 +11430,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity() // Accumulate this attachment's complexity accumulateComplexityComponent(cache_iter->second, total_cost, - all_textures, hud_complexity_list, object_complexity_list); + hud_complexity_list, object_complexity_list); } // Remove stale cache entries @@ -11571,6 +11439,21 @@ void LLVOAvatar::calculateUpdateRenderComplexity() mComplexityCache.erase(*it); } + if (total_cost != mVisualComplexity) + { + LL_DEBUGS("AvatarRender") << "Avatar " << getID() + << " complexity updated was " << mVisualComplexity << " now " << total_cost + << " reported " << mReportedVisualComplexity + << LL_ENDL; + } + else + { + LL_DEBUGS("AvatarRender") << "Avatar " << getID() + << " complexity updated no change " << mVisualComplexity + << " reported " << mReportedVisualComplexity + << LL_ENDL; + } + // Store results mVisualComplexity = total_cost; @@ -11583,7 +11466,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity() U32 LLVOAvatar::calculateBodyPartsComplexity() { - static const U32 COMPLEXITY_BODY_PART_COST = 200; + constexpr U32 COMPLEXITY_BODY_PART_COST = 200; U32 cost = 0; for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++) { @@ -11615,7 +11498,7 @@ U32 LLVOAvatar::calculateBodyPartsComplexity() return cost; } -void LLVOAvatar::processComplexityCostChange(hud_complexity_list_t hud_complexity_list, object_complexity_list_t object_complexity_list) +void LLVOAvatar::processComplexityCostChange(const hud_complexity_list_t &hud_complexity_list, const object_complexity_list_t &object_complexity_list) { static LLCachedControl show_my_complexity_changes(gSavedSettings, "ShowMyComplexityChanges", 20); @@ -11660,8 +11543,8 @@ void LLVOAvatar::accountRenderComplexityForObject( U32& visible_triangle_count, F32& est_triangle_count, F32& surface_area, - hud_complexity_list_t& hud_complexity_list, - object_complexity_list_t& object_complexity_list) + LLHUDComplexity& hud_object_complexity, + LLObjectComplexity& object_complexity) { LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; if (attached_object && !attached_object->isHUDAttachment()) @@ -11723,11 +11606,9 @@ void LLVOAvatar::accountRenderComplexityForObject( if (isSelf()) { - LLObjectComplexity object_complexity; object_complexity.objectName = attached_object->getAttachmentItemName(); object_complexity.objectId = attached_object->getAttachmentItemID(); object_complexity.objectCost = (U32)attachment_total_cost; - object_complexity_list.push_back(object_complexity); } } } @@ -11745,7 +11626,6 @@ void LLVOAvatar::accountRenderComplexityForObject( if (volume) { bool is_rigged_mesh = volume->isRiggedMeshFast(); - LLHUDComplexity hud_object_complexity; hud_object_complexity.objectName = attached_object->getAttachmentItemName(); hud_object_complexity.objectId = attached_object->getAttachmentItemID(); std::string joint_name; @@ -11800,106 +11680,7 @@ void LLVOAvatar::accountRenderComplexityForObject( } } } - hud_complexity_list.push_back(hud_object_complexity); - } - } -} - -// Calculations for mVisualComplexity value -void LLVOAvatar::calculateUpdateRenderComplexityLegacy() -{ - /***************************************************************** - * This calculation should not be modified by third party viewers, - * since it is used to limit rendering and should be uniform for - * everyone. If you have suggested improvements, submit them to - * the official viewer for consideration. - *****************************************************************/ - if (mVisualComplexityStale) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; - - static LLCachedControl max_complexity_setting(gSavedSettings, "MaxAttachmentComplexity"); - F32 max_attachment_complexity = max_complexity_setting; - max_attachment_complexity = llmax(max_attachment_complexity, DEFAULT_MAX_ATTACHMENT_COMPLEXITY); - - // Diagnostic list of all textures on our avatar - static std::unordered_set all_textures; - - U32 cost = VISUAL_COMPLEXITY_UNKNOWN; - LLVOVolume::texture_cost_t textures; - hud_complexity_list_t hud_complexity_list; - object_complexity_list_t object_complexity_list; - - cost += calculateBodyPartsComplexity(); - - mAttachmentVisibleTriangleCount = 0; - mAttachmentEstTriangleCount = 0.f; - mAttachmentSurfaceArea = 0.f; - - // A standalone animated object needs to be accounted for - // using its associated volume. Attached animated objects - // will be covered by the subsequent loop over attachments. - LLControlAvatar *control_av = dynamic_cast(this); - if (control_av) - { - LLVOVolume *volp = control_av->mRootVolp; - if (volp && !volp->isAttachment()) - { - accountRenderComplexityForObject( - volp, - max_attachment_complexity, - textures, - cost, - mAttachmentVisibleTriangleCount, - mAttachmentEstTriangleCount, - mAttachmentSurfaceArea, - hud_complexity_list, - object_complexity_list); - } } - - // Account for complexity of all attachments. - for (attachment_map_t::const_iterator attachment_point = mAttachmentPoints.begin(); - attachment_point != mAttachmentPoints.end(); - ++attachment_point) - { - LLViewerJointAttachment* attachment = attachment_point->second; - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - LLViewerObject* attached_object = attachment_iter->get(); - accountRenderComplexityForObject( - attached_object, - max_attachment_complexity, - textures, - cost, - mAttachmentVisibleTriangleCount, - mAttachmentEstTriangleCount, - mAttachmentSurfaceArea, - hud_complexity_list, - object_complexity_list); - } - } - - if ( cost != mVisualComplexity ) - { - LL_DEBUGS("AvatarRender") << "Avatar "<< getID() - << " complexity updated was " << mVisualComplexity << " now " << cost - << " reported " << mReportedVisualComplexity - << LL_ENDL; - } - else - { - LL_DEBUGS("AvatarRender") << "Avatar "<< getID() - << " complexity updated no change " << mVisualComplexity - << " reported " << mReportedVisualComplexity - << LL_ENDL; - } - mVisualComplexity = cost; - mVisualComplexityStale = false; - - processComplexityCostChange(hud_complexity_list, object_complexity_list); } } diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 79a03527fb..580d6ec911 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -305,15 +305,13 @@ public: U32& visible_triangle_count, F32& est_triangle_count, F32& surface_area, - hud_complexity_list_t& hud_complexity_list, - object_complexity_list_t& object_complexity_list); + LLHUDComplexity& hud_object_complexity, + LLObjectComplexity& object_complexity); void calculateUpdateRenderComplexity(); - void calculateUpdateRenderComplexityLegacy(); static const U32 VISUAL_COMPLEXITY_UNKNOWN; void updateVisualComplexity(); // Mark that an attachment needs complexity recalculation - void markAttachmentComplexityDirty(const LLUUID& object_id); - void markAllComplexityDirty(); + void markAttachmentComplexityDirty(const LLUUID& object_id, bool force_reset_attachment = false); void markBodyPartsComplexityDirty(); void placeProfileQuery(); @@ -650,16 +648,13 @@ private: void accumulateComplexityComponent(const ComplexityComponent& component, U32& total_cost, - LLVOVolume::texture_cost_t& all_textures, hud_complexity_list_t& hud_list, object_complexity_list_t& object_list); bool shouldUpdateComplexityComponent(const ComplexityComponent& component) const; - void updateDirtyAttachmentComplexity(const F32 max_attachment_complexity); - void performFullComplexityUpdate(const F32 max_attachment_complexity); void performPartialComplexityUpdate(const F32 max_attachment_complexity); - void processComplexityCostChange(hud_complexity_list_t hud_complexity_list, object_complexity_list_t object_complexity_list); + void processComplexityCostChange(const hud_complexity_list_t &hud_complexity_list, const object_complexity_list_t &object_complexity_list); // Todo: probably safe to store by local instead of global id // since they should be unique to this avatar, but local id might be not known. @@ -667,12 +662,6 @@ private: complexity_cache_map_t mComplexityCache; // Cache per-attachment complexity ComplexityComponent mBodyPartsComplexity; // Cache for body parts (mesh, eyes, hair, etc) ComplexityComponent mControlAvatarComplexity; // Cache for animated object control avatar - F64 mLastFullComplexityUpdate; - - // Attachments marked for update, - // Todo: probably safe to store by local instead of global id - // since they should be unique to this avatar, but local id might be not known. - std::set mDirtyComplexityAttachments; // the isTooComplex method uses these mutable values to avoid recalculating too frequently // DEPRECATED -- obsolete avatar render cost values diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 1cb6d2ecc5..3b41ccb6fc 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1276,9 +1276,9 @@ void LLVOVolume::updateVisualComplexity() if (rigged_avatar && (rigged_avatar != avatar)) { // This might be wrong. Control avatars update each run, - // due to lack of dirty mechanics and this might be + // due to lack of dirty mechanics, and this might be // where we should implement and call - // markCotrolAvatarComplexityDirty() if !isAttachment(). + // markControlAvatarComplexityDirty() if !isAttachment(). rigged_avatar->markAttachmentComplexityDirty(lobj->getID()); } /*LLVOAvatar* avatar = getAvatarAncestor(); -- cgit v1.3 From 5b485bdc6046867e8b03906e8bdd0f02471f9fa8 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Fri, 3 Apr 2026 02:11:15 +0300 Subject: #5612 Improve profiling coverage --- indra/llmessage/llassetstorage.cpp | 3 ++- indra/newview/llfloaterinspect.cpp | 1 + indra/newview/llremoteparcelrequest.cpp | 1 + indra/newview/llviewergenericmessage.cpp | 3 +++ indra/newview/llviewermessage.cpp | 8 ++++++++ indra/newview/llviewerobject.cpp | 1 + indra/newview/llvoavatar.cpp | 1 + indra/newview/llworld.cpp | 1 + indra/newview/llworldmapmessage.cpp | 2 ++ 9 files changed, 20 insertions(+), 1 deletion(-) (limited to 'indra/newview/llvoavatar.cpp') diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index 4c3acb27f4..b6a98575f9 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -453,6 +453,7 @@ bool LLAssetStorage::findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetTyp bool exists = LLFileSystem::getExists(uuid, type); if (exists) { + LL_PROFILE_ZONE_SCOPED; LLFileSystem file(uuid, type); U32 size = file.getSize(); if (size > 0) @@ -562,7 +563,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, if (callback == tmp->mDownCallback && user_data == tmp->mUserData) { // this is a duplicate from the same subsystem - throw it away - LL_WARNS("AssetStorage") << "Discarding duplicate request for asset " << uuid + LL_DEBUGS("AssetStorage") << "Discarding duplicate request for asset " << uuid << "." << LLAssetType::lookup(type) << LL_ENDL; return; } diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp index c0fe7ad896..163edf0426 100644 --- a/indra/newview/llfloaterinspect.cpp +++ b/indra/newview/llfloaterinspect.cpp @@ -100,6 +100,7 @@ void LLFloaterInspect::onOpen(const LLSD& key) LLSelectMgr::getInstance()->setForceSelection(forcesel); // restore previouis value mObjectSelection = LLSelectMgr::getInstance()->getSelection(); refresh(); + mDirty = false; } void LLFloaterInspect::onClickCreatorProfile() { diff --git a/indra/newview/llremoteparcelrequest.cpp b/indra/newview/llremoteparcelrequest.cpp index f89afd38ab..c1b33f313b 100644 --- a/indra/newview/llremoteparcelrequest.cpp +++ b/indra/newview/llremoteparcelrequest.cpp @@ -84,6 +84,7 @@ void LLRemoteParcelInfoProcessor::removeObserver(const LLUUID& parcel_id, LLRemo //static void LLRemoteParcelInfoProcessor::processParcelInfoReply(LLMessageSystem* msg, void**) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; LLParcelData parcel_data; msg->getUUID ("Data", "ParcelID", parcel_data.parcel_id); diff --git a/indra/newview/llviewergenericmessage.cpp b/indra/newview/llviewergenericmessage.cpp index fd894a5997..f3a0f026b8 100644 --- a/indra/newview/llviewergenericmessage.cpp +++ b/indra/newview/llviewergenericmessage.cpp @@ -73,6 +73,7 @@ void send_generic_message(const std::string& method, void process_generic_message(LLMessageSystem* msg, void**) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; LLUUID agent_id; msg->getUUID("AgentData", "AgentID", agent_id); if (agent_id != gAgent.getID()) @@ -95,6 +96,7 @@ void process_generic_message(LLMessageSystem* msg, void**) void process_generic_streaming_message(LLMessageSystem* msg, void**) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; LLGenericStreamingMessage data; data.unpack(msg); switch (data.mMethod) @@ -110,6 +112,7 @@ void process_generic_streaming_message(LLMessageSystem* msg, void**) void process_large_generic_message(LLMessageSystem* msg, void**) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; LLUUID agent_id; msg->getUUID("AgentData", "AgentID", agent_id); if (agent_id != gAgent.getID()) diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 5d8bd45218..812ba76551 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3554,6 +3554,7 @@ extern U32Bits gObjectData; void process_object_update(LLMessageSystem *mesgsys, void **user_data) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; // Update the data counters if (mesgsys->getReceiveCompressedSize()) { @@ -3575,6 +3576,7 @@ void process_object_update(LLMessageSystem *mesgsys, void **user_data) void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; // Update the data counters if (mesgsys->getReceiveCompressedSize()) { @@ -3596,6 +3598,7 @@ void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; // Update the data counters if (mesgsys->getReceiveCompressedSize()) { @@ -3613,6 +3616,7 @@ void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data) void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_data) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; if (mesgsys->getReceiveCompressedSize()) { gObjectData += (U32Bytes)mesgsys->getReceiveCompressedSize(); @@ -3972,6 +3976,7 @@ void process_sim_stats(LLMessageSystem *msg, void **user_data) void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; LLUUID animation_id; LLUUID uuid; S32 anim_sequence_id; @@ -4083,6 +4088,7 @@ void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data) void process_object_animation(LLMessageSystem *mesgsys, void **user_data) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; LLUUID animation_id; LLUUID uuid; S32 anim_sequence_id; @@ -4148,6 +4154,7 @@ void process_object_animation(LLMessageSystem *mesgsys, void **user_data) void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; LLUUID uuid; mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid); @@ -5679,6 +5686,7 @@ void process_script_experience_details(const LLSD& experience_details, LLSD args void process_script_question(LLMessageSystem *msg, void **user_data) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; // *TODO: Translate owner name -> [FIRST] [LAST] LLHost sender = msg->getSender(); diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 521c6731e0..7c26cb3c9f 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -3210,6 +3210,7 @@ S32 LLFilenameAndTask::sCount = 0; // static void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; LLUUID task_id; msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_TaskID, task_id); LLViewerObject* object = gObjectList.findObject(task_id); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index bb152ced40..2f39a76156 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -5984,6 +5984,7 @@ const LLUUID& LLVOAvatar::getStepSound() const //----------------------------------------------------------------------------- void LLVOAvatar::processAnimationStateChanges() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; if ( isAnyAnimationSignaled(AGENT_WALK_ANIMS, NUM_AGENT_WALK_ANIMS) ) { startMotion(ANIM_AGENT_WALK_ADJUST); diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 47e1815bc2..d02694de7d 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -1217,6 +1217,7 @@ void process_disable_simulator(LLMessageSystem *mesgsys, void **user_data) void process_region_handshake(LLMessageSystem* msg, void** user_data) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; LLHost host = msg->getSender(); LLViewerRegion* regionp = LLWorld::getInstance()->getRegion(host); if (!regionp) diff --git a/indra/newview/llworldmapmessage.cpp b/indra/newview/llworldmapmessage.cpp index 3264f8ae8b..c039f9de3f 100644 --- a/indra/newview/llworldmapmessage.cpp +++ b/indra/newview/llworldmapmessage.cpp @@ -154,6 +154,7 @@ void LLWorldMapMessage::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 // public static void LLWorldMapMessage::processMapBlockReply(LLMessageSystem* msg, void**) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; if (gNonInteractive) { return; @@ -248,6 +249,7 @@ void LLWorldMapMessage::processMapBlockReply(LLMessageSystem* msg, void**) // public static void LLWorldMapMessage::processMapItemReply(LLMessageSystem* msg, void**) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_NETWORK; //LL_INFOS("WorldMap") << LL_ENDL; U32 type; msg->getU32Fast(_PREHASH_RequestData, _PREHASH_ItemType, type); -- cgit v1.3 From 02c7ee34d4640bef3204ccd4207cfae1fd2a9e27 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Tue, 16 Jun 2026 23:10:59 +0300 Subject: #5579 Ensure own avatar's complexity is up to date --- indra/newview/llvoavatar.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'indra/newview/llvoavatar.cpp') diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 2f39a76156..460570d0e5 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -8575,6 +8575,10 @@ bool LLVOAvatar::processFullyLoadedChange(bool loading) if (changed && isSelf()) { + // Agent's own avatar doesn't track bakes the same way as other avatars. + // So just update here, on cloud removal. + markBodyPartsComplexityDirty(); + // to know about outfit switching LLAvatarRenderNotifier::getInstance()->updateNotificationState(); } @@ -10170,6 +10174,10 @@ void LLVOAvatar::onInitialBakedTextureLoaded( bool success, LLViewerFetchedTextu } if (final || !success ) { + if (selfp) + { + selfp->markBodyPartsComplexityDirty(); + } delete avatar_idp; } } @@ -11458,7 +11466,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity() // Store results mVisualComplexity = total_cost; - // Call the existing reporting function with the aggregated lists + // Call the reporting function with the aggregated lists processComplexityCostChange(hud_complexity_list, object_complexity_list); // Stop processing until something changes -- cgit v1.3