summaryrefslogtreecommitdiff
path: root/indra/newview/llvoavatar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llvoavatar.cpp')
-rwxr-xr-xindra/newview/llvoavatar.cpp372
1 files changed, 274 insertions, 98 deletions
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 06fb23b84b..3c25339037 100755
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -58,6 +58,7 @@
#include "llhudmanager.h"
#include "llhudnametag.h"
#include "llhudtext.h" // for mText/mDebugText
+#include "llimview.h"
#include "llinitparam.h"
#include "llkeyframefallmotion.h"
#include "llkeyframestandmotion.h"
@@ -668,6 +669,9 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mSpecialRenderMode(0),
mAttachmentGeometryBytes(0),
mAttachmentSurfaceArea(0.f),
+ mReportedVisualComplexity(-1),
+ mReportedAttachmentGeometryBytes(-1),
+ mReportedAttachmentSurfaceArea(-1.f),
mTurning(FALSE),
mLastSkeletonSerialNum( 0 ),
mIsSitting(FALSE),
@@ -701,6 +705,8 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mFullyLoaded(FALSE),
mPreviousFullyLoaded(FALSE),
mFullyLoadedInitialized(FALSE),
+ mVisualComplexity(0),
+ mVisualComplexityStale(TRUE),
mLoadedCallbacksPaused(FALSE),
mHasPelvisOffset( FALSE ),
mRenderUnloadedAvatar(LLCachedControl<bool>(gSavedSettings, "RenderUnloadedAvatar")),
@@ -769,6 +775,13 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mLastPelvisToFoot = 0.0f;
mPelvisFixup = 0.0f;
mLastPelvisFixup = 0.0f;
+
+ mCachedVisualMute = !isSelf();
+ mCachedVisualMuteUpdateTime = LLFrameTimer::getTotalSeconds() + 5.0;
+ mVisuallyMuteSetting = VISUAL_MUTE_NOT_SET;
+
+ F32 color_value = (F32) (getID().mData[0]);
+ mMutedAVColor = calcMutedAVColor(color_value, 0, 256);
}
std::string LLVOAvatar::avString() const
@@ -2971,14 +2984,82 @@ void LLVOAvatar::slamPosition()
mRoot->updateWorldMatrixChildren();
}
-bool LLVOAvatar::isVisuallyMuted() const
+bool LLVOAvatar::isVisuallyMuted()
{
- static LLCachedControl<U32> max_attachment_bytes(gSavedSettings, "RenderAutoMuteByteLimit");
- static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit");
-
- return LLMuteList::getInstance()->isMuted(getID())
- || (mAttachmentGeometryBytes > max_attachment_bytes && max_attachment_bytes > 0)
- || (mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f);
+ bool muted = false;
+
+ // Priority order (highest priority first)
+ // * own avatar is never visually muted
+ // * if on the "always draw normally" list, draw them normally
+ // * if on the "always visually mute" list, mute them
+ // * draw them normally if they meet the following criteria:
+ // - within the closest N avatars OR on friends list OR in an IM chat
+ // - AND aren't over the thresholds
+ // * otherwise visually mute all other avatars
+
+ if (!isSelf())
+ {
+ static LLCachedControl<bool> render_mute_enabled(gSavedSettings, "RenderAutoMuteEnabled");
+ static LLCachedControl<U32> max_attachment_bytes(gSavedSettings, "RenderAutoMuteByteLimit");
+ static LLCachedControl<F32> max_attachment_area(gSavedSettings, "RenderAutoMuteSurfaceAreaLimit");
+ static LLCachedControl<U32> max_render_cost(gSavedSettings, "RenderAutoMuteRenderCostLimit");
+ static LLCachedControl<U32> visibility_rank(gSavedSettings, "RenderAutoMuteVisibilityRank");
+
+ if (mVisuallyMuteSetting == ALWAYS_VISUAL_MUTE)
+ { // Always want to see this AV as an imposter
+ muted = true;
+ }
+ else if (mVisuallyMuteSetting == NEVER_VISUAL_MUTE)
+ { // Never show as imposter
+ muted = false;
+ }
+ else if (render_mute_enabled)
+ {
+ F64 now = LLFrameTimer::getTotalSeconds();
+
+ if (now < mCachedVisualMuteUpdateTime)
+ { // Use cached mute value
+ muted = mCachedVisualMute;
+ }
+ else
+ { // Determine if visually muted or not
+
+ U32 max_cost = (U32) (max_render_cost*(LLVOAvatar::sLODFactor+0.5));
+
+ muted = LLMuteList::getInstance()->isMuted(getID()) ||
+ (mAttachmentGeometryBytes > max_attachment_bytes && max_attachment_bytes > 0) ||
+ (mAttachmentSurfaceArea > max_attachment_area && max_attachment_area > 0.f) ||
+ (mVisualComplexity > max_cost && max_render_cost > 0);
+
+ // Could be part of the grand || collection above, but yanked out to make the logic visible
+ if (!muted)
+ {
+ if (visibility_rank > 0)
+ { // They are above the visibilty rank - mute them
+ muted = (mVisibilityRank > visibility_rank);
+ }
+
+ if (muted || // Don't mute friends or IMs
+ visibility_rank == 0)
+ {
+ muted = !(LLAvatarTracker::instance().isBuddy(getID()));
+ if (muted)
+ { // Not a friend, so they are muted ... are they in an IM?
+ LLUUID session_id = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL,getID());
+ muted = !gIMMgr->hasSession(session_id);
+ }
+ }
+ }
+
+ // Save visual mute state and set interval for updating
+ const F64 SECONDS_BETWEEN_RENDER_AUTO_MUTE_UPDATES = 1.5;
+ mCachedVisualMuteUpdateTime = now + SECONDS_BETWEEN_RENDER_AUTO_MUTE_UPDATES;
+ mCachedVisualMute = muted;
+ }
+ }
+ }
+
+ return muted;
}
//------------------------------------------------------------------------
@@ -3076,7 +3157,8 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
// the rest should only be done occasionally for far away avatars
//--------------------------------------------------------------------
- if (visible && (!isSelf() || isVisuallyMuted()) && !mIsDummy && sUseImpostors && !mNeedsAnimUpdate && !sFreezeCounter)
+ bool visually_muted = isVisuallyMuted();
+ if (visible && (!isSelf() || visually_muted) && !mIsDummy && sUseImpostors && !mNeedsAnimUpdate && !sFreezeCounter)
{
const LLVector4a* ext = mDrawable->getSpatialExtents();
LLVector4a size;
@@ -3085,8 +3167,8 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
F32 impostor_area = 256.f*512.f*(8.125f - LLVOAvatar::sLODFactor*8.f);
- if (isVisuallyMuted())
- { // muted avatars update at 16 hz
+ if (visually_muted)
+ { // visually muted avatars update at 16 hz
mUpdatePeriod = 16;
}
else if (mVisibilityRank <= LLVOAvatar::sMaxVisible ||
@@ -3886,9 +3968,11 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
BOOL first_pass = TRUE;
if (!LLDrawPoolAvatar::sSkipOpaque)
{
+ bool visually_muted = isVisuallyMuted();
+
if (!isSelf() || gAgent.needsRenderHead() || LLPipeline::sShadowRender)
{
- if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy)
+ if (isTextureVisible(TEX_HEAD_BAKED) || mIsDummy || visually_muted)
{
LLViewerJoint* head_mesh = getViewerJoint(MESH_ID_HEAD);
if (head_mesh)
@@ -3898,7 +3982,7 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
first_pass = FALSE;
}
}
- if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy)
+ if (isTextureVisible(TEX_UPPER_BAKED) || mIsDummy || visually_muted)
{
LLViewerJoint* upper_mesh = getViewerJoint(MESH_ID_UPPER_BODY);
if (upper_mesh)
@@ -3908,7 +3992,7 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
first_pass = FALSE;
}
- if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy)
+ if (isTextureVisible(TEX_LOWER_BAKED) || mIsDummy || visually_muted)
{
LLViewerJoint* lower_mesh = getViewerJoint(MESH_ID_LOWER_BODY);
if (lower_mesh)
@@ -5480,6 +5564,8 @@ const LLViewerJointAttachment *LLVOAvatar::attachObject(LLViewerObject *viewer_o
return 0;
}
+ mVisualComplexityStale = TRUE;
+
if (viewer_object->isSelected())
{
LLSelectMgr::getInstance()->updateSelectionCenter();
@@ -5627,6 +5713,7 @@ BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object)
if (attachment->isObjectAttached(viewer_object))
{
+ mVisualComplexityStale = TRUE;
cleanupAttachedMesh( viewer_object );
attachment->removeObject(viewer_object);
lldebugs << "Detaching object " << viewer_object->mID << " from " << attachment->getName() << llendl;
@@ -7728,9 +7815,9 @@ void LLVOAvatar::updateImpostors()
LLCharacter::sAllowInstancesChange = TRUE ;
}
-BOOL LLVOAvatar::isImpostor() const
+BOOL LLVOAvatar::isImpostor()
{
- return (isVisuallyMuted() || (sUseImpostors && mUpdatePeriod >= IMPOSTOR_PERIOD)) ? TRUE : FALSE;
+ return sUseImpostors && (isVisuallyMuted() || (mUpdatePeriod >= IMPOSTOR_PERIOD)) ? TRUE : FALSE;
}
@@ -7773,135 +7860,224 @@ void LLVOAvatar::getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& d
angle.mV[2] = da;
}
+
void LLVOAvatar::idleUpdateRenderCost()
{
- static const U32 ARC_BODY_PART_COST = 200;
- static const U32 ARC_LIMIT = 20000;
+ static LLCachedControl<U32> max_render_cost(gSavedSettings, "RenderAutoMuteRenderCostLimit");
- static std::set<LLUUID> all_textures;
+ static const U32 ARC_LIMIT = 20000;
if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_ATTACHMENT_BYTES))
{ //set debug text to attachment geometry bytes here so render cost will override
setDebugText(llformat("%.1f KB, %.2f m^2", mAttachmentGeometryBytes/1024.f, mAttachmentSurfaceArea));
}
- if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHAME))
+ if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHAME) && max_render_cost == 0)
{
return;
}
- U32 cost = 0;
- LLVOVolume::texture_cost_t textures;
+ calculateUpdateRenderCost(); // Update mVisualComplexity if needed
+
+ doRenderCostNagging(max_render_cost); // Remind the user their AV is too complex
- for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHAME))
+ {
+ std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus());
+ setDebugText(llformat("%s %d", viz_string.c_str(), mVisualComplexity));
+ F32 green = 1.f-llclamp(((F32) mVisualComplexity-(F32)ARC_LIMIT)/(F32)ARC_LIMIT, 0.f, 1.f);
+ F32 red = llmin((F32) mVisualComplexity/(F32)ARC_LIMIT, 1.f);
+ mText->setColor(LLColor4(red,green,0,1));
+ }
+}
+
+
+// Remind the user about their expensive avatar
+void LLVOAvatar::doRenderCostNagging(U32 max_render_cost)
+{
+ if (isSelf())
{
- const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)baked_index);
- ETextureIndex tex_index = baked_dict->mTextureIndex;
- if ((tex_index != TEX_SKIRT_BAKED) || (isWearingWearableType(LLWearableType::WT_SKIRT)))
+ static S32 sOldComplexity = 0;
+ static F64 sLastRenderCostNagTime = 0.0;
+
+ const F64 RENDER_NAG_INTERVAL = 60.0;
+
+ F64 now = LLFrameTimer::getTotalSeconds();
+ if (sLastRenderCostNagTime > 0.0 &&
+ (now - sLastRenderCostNagTime) > RENDER_NAG_INTERVAL &&
+ sOldComplexity != mVisualComplexity)
{
- if (isTextureVisible(tex_index))
- {
- cost +=ARC_BODY_PART_COST;
+ sOldComplexity = mVisualComplexity;
+
+ if (max_render_cost > 0)
+ { //pop up notification that you have exceeded a render cost limit
+ if (mVisualComplexity > max_render_cost+max_render_cost/2)
+ {
+ LLNotificationsUtil::add("ExceededHighDetailRenderCost");
+ sLastRenderCostNagTime = now;
+ }
+ else if (mVisualComplexity > max_render_cost)
+ {
+ LLNotificationsUtil::add("ExceededMidDetailRenderCost");
+ sLastRenderCostNagTime = now;
+ }
+ else if (mVisualComplexity > max_render_cost/2)
+ {
+ LLNotificationsUtil::add("ExceededLowDetailRenderCost");
+ sLastRenderCostNagTime = now;
+ }
}
}
}
+}
- for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin();
- iter != mAttachmentPoints.end();
- ++iter)
+// Calculations for mVisualComplexity value
+void LLVOAvatar::calculateUpdateRenderCost()
+{
+ static const U32 ARC_BODY_PART_COST = 200;
+
+ // Diagnostic list of all textures on our avatar
+ static std::set<LLUUID> all_textures;
+
+ if (mVisualComplexityStale)
{
- LLViewerJointAttachment* attachment = iter->second;
- for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
- attachment_iter != attachment->mAttachedObjects.end();
- ++attachment_iter)
+ mVisualComplexityStale = FALSE;
+ U32 cost = 0;
+ LLVOVolume::texture_cost_t textures;
+
+ for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
{
- const LLViewerObject* attached_object = (*attachment_iter);
- if (attached_object && !attached_object->isHUDAttachment())
+ const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = LLAvatarAppearanceDictionary::getInstance()->getBakedTexture((EBakedTextureIndex)baked_index);
+ ETextureIndex tex_index = baked_dict->mTextureIndex;
+ if ((tex_index != TEX_SKIRT_BAKED) || (isWearingWearableType(LLWearableType::WT_SKIRT)))
{
- textures.clear();
- const LLDrawable* drawable = attached_object->mDrawable;
- if (drawable)
+ if (isTextureVisible(tex_index))
{
- const LLVOVolume* volume = drawable->getVOVolume();
- if (volume)
- {
- cost += volume->getRenderCost(textures);
+ cost +=ARC_BODY_PART_COST;
+ }
+ }
+ }
+
- const_child_list_t children = volume->getChildren();
- for (const_child_list_t::const_iterator child_iter = children.begin();
- child_iter != children.end();
- ++child_iter)
+ for (attachment_map_t::const_iterator iter = mAttachmentPoints.begin();
+ iter != mAttachmentPoints.end();
+ ++iter)
+ {
+ LLViewerJointAttachment* attachment = iter->second;
+ for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin();
+ attachment_iter != attachment->mAttachedObjects.end();
+ ++attachment_iter)
+ {
+ const LLViewerObject* attached_object = (*attachment_iter);
+ if (attached_object && !attached_object->isHUDAttachment())
+ {
+ textures.clear();
+ const LLDrawable* drawable = attached_object->mDrawable;
+ if (drawable)
+ {
+ const LLVOVolume* volume = drawable->getVOVolume();
+ if (volume)
{
- LLViewerObject* child_obj = *child_iter;
- LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj );
- if (child)
+ cost += volume->getRenderCost(textures);
+
+ const_child_list_t children = volume->getChildren();
+ for (const_child_list_t::const_iterator child_iter = children.begin();
+ child_iter != children.end();
+ ++child_iter)
{
- cost += child->getRenderCost(textures);
+ LLViewerObject* child_obj = *child_iter;
+ LLVOVolume *child = dynamic_cast<LLVOVolume*>( child_obj );
+ if (child)
+ {
+ cost += child->getRenderCost(textures);
+ }
}
- }
- for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
- {
- // add the cost of each individual texture in the linkset
- cost += iter->second;
+ for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
+ {
+ // add the cost of each individual texture in the linkset
+ cost += iter->second;
+ }
}
}
}
}
- }
-
- }
-
+ }
- // Diagnostic output to identify all avatar-related textures.
- // Does not affect rendering cost calculation.
- // Could be wrapped in a debug option if output becomes problematic.
- if (isSelf())
- {
- // print any attachment textures we didn't already know about.
- for (LLVOVolume::texture_cost_t::iterator it = textures.begin(); it != textures.end(); ++it)
+ // Diagnostic output to identify all avatar-related textures.
+ // Does not affect rendering cost calculation.
+ // Could be wrapped in a debug option if output becomes problematic.
+ if (isSelf())
{
- LLUUID image_id = it->first;
- if( image_id.isNull() || image_id == IMG_DEFAULT || image_id == IMG_DEFAULT_AVATAR)
- continue;
- if (all_textures.find(image_id) == all_textures.end())
+ // print any attachment textures we didn't already know about.
+ for (LLVOVolume::texture_cost_t::iterator it = textures.begin(); it != textures.end(); ++it)
{
- // attachment texture not previously seen.
- llinfos << "attachment_texture: " << image_id.asString() << llendl;
- all_textures.insert(image_id);
+ LLUUID image_id = it->first;
+ if( image_id.isNull() || image_id == IMG_DEFAULT || image_id == IMG_DEFAULT_AVATAR)
+ continue;
+ if (all_textures.find(image_id) == all_textures.end())
+ {
+ // attachment texture not previously seen.
+ llinfos << "attachment_texture: " << image_id.asString() << llendl;
+ all_textures.insert(image_id);
+ }
}
- }
- // print any avatar textures we didn't already know about
- for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin();
+ // print any avatar textures we didn't already know about
+ for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin();
iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end();
- ++iter)
- {
- const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second;
- // TODO: MULTI-WEARABLE: handle multiple textures for self
- const LLViewerTexture* te_image = getImage(iter->first,0);
- if (!te_image)
- continue;
- LLUUID image_id = te_image->getID();
- if( image_id.isNull() || image_id == IMG_DEFAULT || image_id == IMG_DEFAULT_AVATAR)
- continue;
- if (all_textures.find(image_id) == all_textures.end())
+ ++iter)
{
- llinfos << "local_texture: " << texture_dict->mName << ": " << image_id << llendl;
- all_textures.insert(image_id);
+ const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second;
+ // TODO: MULTI-WEARABLE: handle multiple textures for self
+ const LLViewerTexture* te_image = getImage(iter->first,0);
+ if (!te_image)
+ continue;
+ LLUUID image_id = te_image->getID();
+ if( image_id.isNull() || image_id == IMG_DEFAULT || image_id == IMG_DEFAULT_AVATAR)
+ continue;
+ if (all_textures.find(image_id) == all_textures.end())
+ {
+ llinfos << "local_texture: " << texture_dict->mName << ": " << image_id << llendl;
+ all_textures.insert(image_id);
+ }
}
}
+
+ mVisualComplexity = cost;
}
+}
-
- std::string viz_string = LLVOAvatar::rezStatusToString(getRezzedStatus());
- setDebugText(llformat("%s %d", viz_string.c_str(), cost));
- mVisualComplexity = cost;
- F32 green = 1.f-llclamp(((F32) cost-(F32)ARC_LIMIT)/(F32)ARC_LIMIT, 0.f, 1.f);
- F32 red = llmin((F32) cost/(F32)ARC_LIMIT, 1.f);
- mText->setColor(LLColor4(red,green,0,1));
+
+// static
+LLColor4 LLVOAvatar::calcMutedAVColor(F32 value, S32 range_low, S32 range_high)
+{
+ F32 clamped_value = llmin(value, (F32) range_high);
+ clamped_value = llmax(value, (F32) range_low);
+ F32 spectrum = (clamped_value / range_high); // spectrum is between 0 and 1.f
+
+ // Array of colors. These are arranged so only one RGB color changes between each step,
+ // and it loops back to red so there is an even distribution. It is not a heat map
+ const S32 NUM_SPECTRUM_COLORS = 7;
+ static LLColor4 * spectrum_color[NUM_SPECTRUM_COLORS] = { &LLColor4::red, &LLColor4::magenta, &LLColor4::blue, &LLColor4::cyan, &LLColor4::green, &LLColor4::yellow, &LLColor4::red };
+
+ spectrum = spectrum * (NUM_SPECTRUM_COLORS - 1); // Scale to range of number of colors
+ S32 spectrum_index_1 = floor(spectrum); // Desired color will be after this index
+ S32 spectrum_index_2 = spectrum_index_1 + 1; // and before this index (inclusive)
+ F32 fractBetween = spectrum - (F32)(spectrum_index_1); // distance between the two indexes (0-1)
+
+ LLColor4 new_color = lerp(*spectrum_color[spectrum_index_1], *spectrum_color[spectrum_index_2], fractBetween);
+ new_color.normalize();
+ new_color *= 0.9f;
+
+ //llinfos << "From value " << std::setprecision(3) << value << " returning color " << new_color
+ // << " using indexes " << spectrum_index_1 << ", " << spectrum_index_2
+ // << " and fractBetween " << fractBetween
+ // << llendl;
+
+ return new_color;
}
// static