Massive sunshine/viewer-dev catchup. Further implemented serverside baking and added/updated metrics feedback.

This commit is contained in:
Shyotl
2013-02-19 01:50:46 -06:00
parent c3c34deb10
commit 76f3dc9e19
72 changed files with 3945 additions and 994 deletions

View File

@@ -150,6 +150,10 @@ BOOL LLTexLayerSetBuffer::renderTexLayerSet()
gAlphaMaskProgram.bind();
gAlphaMaskProgram.setMinimumAlpha(0.004f);
}
else
{
gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.00f);
}
LLVertexBuffer::unbind();
@@ -928,7 +932,45 @@ LLWearableType::EType LLTexLayerInterface::getWearableType() const
ETextureIndex te = getLocalTextureIndex();
if (TEX_INVALID == te)
{
return LLWearableType::WT_INVALID;
LLWearableType::EType type = LLWearableType::WT_INVALID;
param_color_list_t::const_iterator color_iter = mParamColorList.begin();
param_alpha_list_t::const_iterator alpha_iter = mParamAlphaList.begin();
for (; color_iter != mParamColorList.end(); color_iter++)
{
LLTexLayerParamColor* param = *color_iter;
if (param)
{
LLWearableType::EType new_type = (LLWearableType::EType)param->getWearableType();
if (new_type != LLWearableType::WT_INVALID && new_type != type)
{
if (type != LLWearableType::WT_INVALID)
{
return LLWearableType::WT_INVALID;
}
type = new_type;
}
}
}
for (; alpha_iter != mParamAlphaList.end(); alpha_iter++)
{
LLTexLayerParamAlpha* param = *alpha_iter;
if (param)
{
LLWearableType::EType new_type = (LLWearableType::EType)param->getWearableType();
if (new_type != LLWearableType::WT_INVALID && new_type != type)
{
if (type != LLWearableType::WT_INVALID)
{
return LLWearableType::WT_INVALID;
}
type = new_type;
}
}
}
return type;
}
return LLAvatarAppearanceDictionary::getTEWearableType(te);
}
@@ -1132,7 +1174,8 @@ BOOL LLTexLayer::render(S32 x, S32 y, S32 width, S32 height)
}
}//*/
renderMorphMasks(x, y, width, height, net_color);
const bool force_render = true;
renderMorphMasks(x, y, width, height, net_color, force_render);
alpha_mask_specified = TRUE;
gGL.flush();
gGL.blendFunc(LLRender::BF_DEST_ALPHA, LLRender::BF_ONE_MINUS_DEST_ALPHA);
@@ -1381,8 +1424,13 @@ BOOL LLTexLayer::blendAlphaTexture(S32 x, S32 y, S32 width, S32 height)
}
static LLFastTimer::DeclareTimer FTM_RENDER_MORPH_MASKS("renderMorphMasks");
BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color)
void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, bool force_render)
{
if (!force_render && !hasMorph())
{
lldebugs << "skipping renderMorphMasks for " << getUUID() << llendl;
return;
}
LLFastTimer t(FTM_RENDER_MORPH_MASKS);
BOOL success = TRUE;
@@ -1419,6 +1467,11 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
{
LLTexLayerParamAlpha* param = *iter;
success &= param->render( x, y, width, height );
if (!success && !force_render)
{
lldebugs << "Failed to render param " << param->getID() << " ; skipping morph mask." << llendl;
return;
}
}
// Approximates a min() function
@@ -1444,25 +1497,29 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
}
}
if( !getInfo()->mStaticImageFileName.empty() )
if( !getInfo()->mStaticImageFileName.empty() && getInfo()->mStaticImageIsMask )
{
LLGLTexture* tex = LLTexLayerStaticImageList::getInstance()->getTexture(getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask);
if( tex )
{
if( (tex->getComponents() == 4) ||
( (tex->getComponents() == 1) && getInfo()->mStaticImageIsMask ) )
if( (tex->getComponents() == 4) || (tex->getComponents() == 1) )
{
LLGLSNoAlphaTest gls_no_alpha_test;
gGL.getTexUnit(0)->bind(tex, TRUE);
gl_rect_2d_simple_tex( width, height );
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
}
else
{
llwarns << "Skipping rendering of " << getInfo()->mStaticImageFileName
<< "; expected 1 or 4 components." << llendl;
}
}
}
// Draw a rectangle with the layer color to multiply the alpha by that color's alpha.
// Note: we're still using gGL.blendFunc( GL_DST_ALPHA, GL_ZERO );
if (layer_color.mV[VW] != 1.f)
if ( !is_approx_equal(layer_color.mV[VW], 1.f) )
{
LLGLDisable no_alpha(GL_ALPHA_TEST);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
@@ -1515,8 +1572,6 @@ BOOL LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
mMorphMasksValid = TRUE;
getTexLayerSet()->applyMorphMask(alpha_data, width, height, 1);
}
return success;
}
static LLFastTimer::DeclareTimer FTM_ADD_ALPHA_MASK("addAlphaMask");
@@ -1531,7 +1586,8 @@ void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32
findNetColor( &net_color );
// TODO: eliminate need for layer morph mask valid flag
invalidateMorphMasks();
renderMorphMasks(originX, originY, width, height, net_color);
const bool force_render = false;
renderMorphMasks(originX, originY, width, height, net_color, force_render);
alphaData = getAlphaData();
}
if (alphaData)
@@ -1540,7 +1596,7 @@ void LLTexLayer::addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32
{
U8 curAlpha = data[i];
U16 resultAlpha = curAlpha;
resultAlpha *= (alphaData[i] + 1);
resultAlpha *= ( ((U16)alphaData[i]) + 1);
resultAlpha = resultAlpha >> 8;
data[i] = (U8)resultAlpha;
}
@@ -1915,9 +1971,15 @@ LLGLTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_name,
{
if( (image_raw->getComponents() == 1) && is_mask )
{
// Note: these are static, unchanging images so it's ok to assume
// that once an image is a mask it's always a mask.
tex->setExplicitFormat( GL_ALPHA8, GL_ALPHA );
// Convert grayscale alpha masks from single channel into RGBA.
// Fill RGB with black to allow fixed function gl calls
// to match shader implementation.
LLPointer<LLImageRaw> alpha_image_raw = image_raw;
image_raw = new LLImageRaw(image_raw->getWidth(),
image_raw->getHeight(),
4);
image_raw->copyUnscaledAlphaMask(alpha_image_raw, LLColor4U::black);
}
tex->createGLTexture(0, image_raw, 0, TRUE, LLGLTexture::LOCAL);

View File

@@ -160,7 +160,7 @@ public:
BOOL findNetColor(LLColor4* color) const;
/*virtual*/ BOOL blendAlphaTexture(S32 x, S32 y, S32 width, S32 height); // Multiplies a single alpha texture against the frame buffer
/*virtual*/ void gatherAlphaMasks(U8 *data, S32 originX, S32 originY, S32 width, S32 height);
BOOL renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color);
void renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLColor4 &layer_color, bool force_render);
void addAlphaMask(U8 *data, S32 originX, S32 originY, S32 width, S32 height);
/*virtual*/ BOOL isInvisibleAlphaMask() const;

View File

@@ -178,10 +178,6 @@ void LLTexLayerParamAlpha::setWeight(F32 weight, BOOL upload_bake)
if ((mAvatarAppearance->getSex() & getSex()) &&
(mAvatarAppearance->isSelf() && !mIsDummy)) // only trigger a baked texture update if we're changing a wearable's visual param.
{
if (mAvatarAppearance->isValid() && mAvatarAppearance->isEditingAppearance())
{
upload_bake = FALSE;
}
mAvatarAppearance->invalidateComposite(mTexLayer->getTexLayerSet(), upload_bake);
mTexLayer->invalidateMorphMasks();
}

View File

@@ -95,7 +95,8 @@ void LLWearableData::setWearable(const LLWearableType::EType type, U32 index, LL
}
U32 LLWearableData::pushWearable(const LLWearableType::EType type,
LLWearable *wearable)
LLWearable *wearable,
bool trigger_updated /* = true */)
{
if (wearable == NULL)
{
@@ -121,8 +122,11 @@ U32 LLWearableData::pushWearable(const LLWearableType::EType type,
mWearableDatas[type].push_back(wearable);
idxWearable = mWearableDatas[type].size() - 1;
}
const BOOL removed = FALSE;
wearableUpdated(wearable, removed);
if (trigger_updated)
{
const BOOL removed = FALSE;
wearableUpdated(wearable, removed);
}
return idxWearable;
// [/RLVa:KB]
}
@@ -175,6 +179,17 @@ void LLWearableData::popWearable(const LLWearableType::EType type, U32 index)
}
}
void LLWearableData::clearWearableType(const LLWearableType::EType type)
{
wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(type);
if (wearable_iter == mWearableDatas.end())
{
return;
}
wearableentry_vec_t& wearable_vec = wearable_iter->second;
wearable_vec.clear();
}
bool LLWearableData::swapWearables(const LLWearableType::EType type, U32 index_a, U32 index_b)
{
wearableentry_map_t::iterator wearable_iter = mWearableDatas.find(type);

View File

@@ -72,10 +72,12 @@ public:
protected:
// Low-level data structure setter - public access is via setWearableItem, etc.
void setWearable(const LLWearableType::EType type, U32 index, LLWearable *wearable);
U32 pushWearable(const LLWearableType::EType type, LLWearable *wearable);
virtual void wearableUpdated(LLWearable *wearable, BOOL removed) = 0;
U32 pushWearable(const LLWearableType::EType type, LLWearable *wearable,
bool trigger_updated = true);
virtual void wearableUpdated(LLWearable *wearable, BOOL removed);
void popWearable(LLWearable *wearable);
void popWearable(const LLWearableType::EType type, U32 index);
void clearWearableType(const LLWearableType::EType type);
bool swapWearables(const LLWearableType::EType type, U32 index_a, U32 index_b);
private:
@@ -88,7 +90,7 @@ public:
LLUUID computeBakedTextureHash(LLAvatarAppearanceDefines::EBakedTextureIndex baked_index,
BOOL generate_valid_hash = TRUE);
protected:
virtual void invalidateBakedTextureHash(LLMD5& hash) const = 0;
virtual void invalidateBakedTextureHash(LLMD5& hash) const {}
//--------------------------------------------------------------------
// Member variables

View File

@@ -127,30 +127,73 @@ BOOL LLHandMotion::onUpdate(F32 time, U8* joint_mask)
mLastTime = time;
requestedHandPose = (eHandPose *)mCharacter->getAnimationData("Hand Pose");
// <edit>
if(requestedHandPose && *requestedHandPose >= NUM_HAND_POSES)
{
llwarns << "requested hand pose >= NUM_HAND_POSES" << llendl;
requestedHandPose = &mCurrentPose;
}
// </edit>
// check to see if requested pose has changed
if (!requestedHandPose)
{
if (mNewPose != HAND_POSE_RELAXED && mNewPose != mCurrentPose)
{
mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f);
// Only set param weight for poses other than
// default (HAND_POSE_SPREAD); HAND_POSE_SPREAD
// is not an animatable morph!
if (mNewPose != HAND_POSE_SPREAD)
{
mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f);
}
// Reset morph weight for current pose back to its
// full extend or it might be stuck somewhere in the middle if a
// pose is requested and the old pose is requested again shortly
// after while still blending to the other pose!
if (mCurrentPose != HAND_POSE_SPREAD)
{
mCharacter->setVisualParamWeight(gHandPoseNames[mCurrentPose], 1.f);
}
// Update visual params now if we won't blend
if (mCurrentPose == HAND_POSE_RELAXED)
{
mCharacter->updateVisualParams();
}
}
mNewPose = HAND_POSE_RELAXED;
}
else
{
// this is a new morph we didn't know about before
if (*requestedHandPose != mNewPose && mNewPose != mCurrentPose && mNewPose != HAND_POSE_SPREAD)
// Sometimes we seem to get garbage here, with poses that are out of bounds.
// So check for a valid pose first.
if (*requestedHandPose >= 0 && *requestedHandPose < NUM_HAND_POSES)
{
mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f);
// This is a new morph we didn't know about before:
// Reset morph weight for both current and new pose
// back their starting values while still blending.
if (*requestedHandPose != mNewPose && mNewPose != mCurrentPose)
{
if (mNewPose != HAND_POSE_SPREAD)
{
mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f);
}
// Reset morph weight for current pose back to its full extend
// or it might be stuck somewhere in the middle if a pose is
// requested and the old pose is requested again shortly after
// while still blending to the other pose!
if (mCurrentPose != HAND_POSE_SPREAD)
{
mCharacter->setVisualParamWeight(gHandPoseNames[mCurrentPose], 1.f);
}
// Update visual params now if we won't blend
if (mCurrentPose == *requestedHandPose)
{
mCharacter->updateVisualParams();
}
}
mNewPose = *requestedHandPose;
}
else
{
llwarns << "Requested hand pose out of range. Ignoring requested pose." << llendl;
}
mNewPose = *requestedHandPose;
}
mCharacter->removeAnimationData("Hand Pose");

View File

@@ -50,6 +50,7 @@ const U32 MAX_MOTION_INSTANCES = 32;
//-----------------------------------------------------------------------------
// Constants and statics
//-----------------------------------------------------------------------------
F32 LLMotionController::sCurrentTimeFactor = 1.f;
LLMotionRegistry LLMotionController::sRegistry;
//-----------------------------------------------------------------------------
@@ -133,7 +134,7 @@ LLMotion *LLMotionRegistry::createMotion( const LLUUID &id )
// Class Constructor
//-----------------------------------------------------------------------------
LLMotionController::LLMotionController()
: mTimeFactor(1.f),
: mTimeFactor(sCurrentTimeFactor),
mCharacter(NULL),
mAnimTime(0.f),
mPrevTimerElapsed(0.f),

View File

@@ -174,6 +174,9 @@ public:
const LLFrameTimer& getFrameTimer() { return mTimer; }
static F32 getCurrentTimeFactor() { return sCurrentTimeFactor; };
static void setCurrentTimeFactor(F32 factor) { sCurrentTimeFactor = factor; };
protected:
// internal operations act on motion instances directly
// as there can be duplicate motions per id during blending overlap
@@ -193,7 +196,8 @@ protected:
void deactivateStoppedMotions();
protected:
F32 mTimeFactor;
F32 mTimeFactor; // 1.f for normal speed
static F32 sCurrentTimeFactor; // Value to use for initialization
static LLMotionRegistry sRegistry;
LLPoseBlender mPoseBlender;

View File

@@ -67,15 +67,6 @@ const S32 LSL_PRIM_TEXGEN = 22;
const S32 LSL_PRIM_POINT_LIGHT = 23;
const S32 LSL_PRIM_CAST_SHADOWS = 24;
const S32 LSL_PRIM_GLOW = 25;
const S32 LSL_PRIM_TEXT = 26;
const S32 LSL_PRIM_NAME = 27;
const S32 LSL_PRIM_DESC = 28;
const S32 LSL_PRIM_ROT_LOCAL = 29;
const S32 LSL_PRIM_PHYSICS_SHAPE_TYPE = 30;
const S32 LSL_PRIM_OMEGA = 32;
const S32 LSL_PRIM_POS_LOCAL = 33;
const S32 LSL_PRIM_LINK_TARGET = 34;
const S32 LSL_PRIM_SLICE = 35;
const S32 LSL_PRIM_TYPE_BOX = 0;
const S32 LSL_PRIM_TYPE_CYLINDER= 1;
@@ -135,15 +126,6 @@ const S32 LSL_PRIM_SCULPT_TYPE_MASK = 7;
const S32 LSL_PRIM_SCULPT_FLAG_INVERT = 64;
const S32 LSL_PRIM_SCULPT_FLAG_MIRROR = 128;
const S32 LSL_PRIM_PHYSICS_SHAPE_PRIM = 0;
const S32 LSL_PRIM_PHYSICS_SHAPE_NONE = 1;
const S32 LSL_PRIM_PHYSICS_SHAPE_CONVEX = 2;
const S32 LSL_DENSITY = 1;
const S32 LSL_FRICTION = 2;
const S32 LSL_RESTITUTION = 4;
const S32 LSL_GRAVITY_MULTIPLIER = 8;
const S32 LSL_ALL_SIDES = -1;
const S32 LSL_LINK_ROOT = 1;
const S32 LSL_LINK_FIRST_CHILD = 2;
@@ -197,16 +179,8 @@ const S32 OBJECT_VELOCITY = 5;
const S32 OBJECT_OWNER = 6;
const S32 OBJECT_GROUP = 7;
const S32 OBJECT_CREATOR = 8;
const S32 OBJECT_RUNNING_SCRIPT_COUNT = 9;
const S32 OBJECT_TOTAL_SCRIPT_COUNT = 10;
const S32 OBJECT_SCRIPT_MEMORY = 11;
const S32 OBJECT_SCRIPT_TIME = 12;
const S32 OBJECT_PRIM_EQUIVALENCE = 13;
const S32 OBJECT_SERVER_COST = 14;
const S32 OBJECT_STREAMING_COST = 15;
const S32 OBJECT_PHYSICS_COST = 16;
// llTextBox() magic token string - yes it's a hack.
// llTextBox() magic token string - yes this is a hack. sue me.
char const* const TEXTBOX_MAGIC_TOKEN = "!!llTextBox!!";
// changed() event flags
@@ -236,155 +210,4 @@ const U32 LSL_STATUS_INTERNAL_ERROR = 1999;
// Start per-function errors below, starting at 2000:
const U32 LSL_STATUS_WHITELIST_FAILED = 2001;
// Memory profiling support
const S32 LSL_PROFILE_SCRIPT_NONE = 0;
const S32 LSL_PROFILE_SCRIPT_MEMORY = 1;
// HTTP responses contents type
const S32 LSL_CONTENT_TYPE_TEXT = 0;
const S32 LSL_CONTENT_TYPE_HTML = 1;
// Ray casting
const S32 LSL_RCERR_UNKNOWN = -1;
const S32 LSL_RCERR_SIM_PERF_LOW = -2;
const S32 LSL_RCERR_CAST_TIME_EXCEEDED = -3;
const S32 LSL_RC_REJECT_TYPES = 0;
const S32 LSL_RC_DETECT_PHANTOM = 1;
const S32 LSL_RC_DATA_FLAGS = 2;
const S32 LSL_RC_MAX_HITS = 3;
const S32 LSL_RC_REJECT_AGENTS = 1;
const S32 LSL_RC_REJECT_PHYSICAL = 2;
const S32 LSL_RC_REJECT_NONPHYSICAL = 4;
const S32 LSL_RC_REJECT_LAND = 8;
const S32 LSL_RC_GET_NORMAL = 1;
const S32 LSL_RC_GET_ROOT_KEY = 2;
const S32 LSL_RC_GET_LINK_NUM = 4;
// Estate management
const S32 LSL_ESTATE_ACCESS_ALLOWED_AGENT_ADD = 4;
const S32 LSL_ESTATE_ACCESS_ALLOWED_AGENT_REMOVE = 8;
const S32 LSL_ESTATE_ACCESS_ALLOWED_GROUP_ADD = 16;
const S32 LSL_ESTATE_ACCESS_ALLOWED_GROUP_REMOVE = 32;
const S32 LSL_ESTATE_ACCESS_BANNED_AGENT_ADD = 64;
const S32 LSL_ESTATE_ACCESS_BANNED_AGENT_REMOVE = 128;
// Key Frame Motion:
const S32 LSL_KFM_COMMAND = 0;
const S32 LSL_KFM_MODE = 1;
const S32 LSL_KFM_DATA = 2;
const S32 LSL_KFM_FORWARD = 0;
const S32 LSL_KFM_LOOP = 1;
const S32 LSL_KFM_PING_PONG = 2;
const S32 LSL_KFM_REVERSE = 3;
const S32 LSL_KFM_ROTATION = 1;
const S32 LSL_KFM_TRANSLATION = 2;
const S32 LSL_KFM_CMD_PLAY = 0;
const S32 LSL_KFM_CMD_STOP = 1;
const S32 LSL_KFM_CMD_PAUSE = 2;
// Second Life Server/12 12.04.30.255166 constants for llGetAgentList
const S32 AGENT_LIST_PARCEL = 1;
const S32 AGENT_LIST_PARCEL_OWNER = 2;
const S32 AGENT_LIST_REGION = 4;
// --- SL Constants ABOVE this line ---
// --- OpenSim / Aurora-Sim constants Below ---
// OpenSim Constants (\OpenSim\Region\ScriptEngine\Shared\Api\Runtime\LSL_Constants.cs)
// Constants for cmWindlight (\OpenSim\Region\ScriptEngine\Shared\Api\Runtime\CM_Constants.cs)
const S32 CHANGED_ANIMATION = 16384;
const S32 PARCEL_DETAILS_CLAIMDATE = 10; // used by OpenSim osSetParcelDetails
const S32 STATS_TIME_DILATION = 0;
const S32 STATS_SIM_FPS = 1;
const S32 STATS_PHYSICS_FPS = 2;
const S32 STATS_AGENT_UPDATES = 3;
const S32 STATS_ROOT_AGENTS = 4;
const S32 STATS_CHILD_AGENTS = 5;
const S32 STATS_TOTAL_PRIMS = 6;
const S32 STATS_ACTIVE_PRIMS = 7;
const S32 STATS_FRAME_MS = 8;
const S32 STATS_NET_MS = 9;
const S32 STATS_PHYSICS_MS = 10;
const S32 STATS_IMAGE_MS = 11;
const S32 STATS_OTHER_MS = 12;
const S32 STATS_IN_PACKETS_PER_SECOND = 13;
const S32 STATS_OUT_PACKETS_PER_SECOND = 14;
const S32 STATS_UNACKED_BYTES = 15;
const S32 STATS_AGENT_MS = 16;
const S32 STATS_PENDING_DOWNLOADS = 17;
const S32 STATS_PENDING_UPLOADS = 18;
const S32 STATS_ACTIVE_SCRIPTS = 19;
const S32 STATS_SCRIPT_LPS = 20;
// osNPC
const S32 OS_NPC_FLY = 0;
const S32 OS_NPC_NO_FLY = 1;
const S32 OS_NPC_LAND_AT_TARGET = 2;
const S32 OS_NPC_SIT_NOW = 0;
const U32 OS_NPC_CREATOR_OWNED = 0x1;
const U32 OS_NPC_NOT_OWNED = 0x2;
const U32 OS_NPC_SENSE_AS_AGENT = 0x4;
const U32 OS_NPC_RUNNING = 4;
// Lightshare / Windlight
const S32 WL_WATER_COLOR = 0;
const S32 WL_WATER_FOG_DENSITY_EXPONENT = 1;
const S32 WL_UNDERWATER_FOG_MODIFIER = 2;
const S32 WL_REFLECTION_WAVELET_SCALE = 3;
const S32 WL_FRESNEL_SCALE = 4;
const S32 WL_FRESNEL_OFFSET = 5;
const S32 WL_REFRACT_SCALE_ABOVE = 6;
const S32 WL_REFRACT_SCALE_BELOW = 7;
const S32 WL_BLUR_MULTIPLIER = 8;
const S32 WL_BIG_WAVE_DIRECTION = 9;
const S32 WL_LITTLE_WAVE_DIRECTION = 10;
const S32 WL_NORMAL_MAP_TEXTURE = 11;
const S32 WL_HORIZON = 12;
const S32 WL_HAZE_HORIZON = 13;
const S32 WL_BLUE_DENSITY = 14;
const S32 WL_HAZE_DENSITY = 15;
const S32 WL_DENSITY_MULTIPLIER = 16;
const S32 WL_DISTANCE_MULTIPLIER = 17;
const S32 WL_MAX_ALTITUDE = 18;
const S32 WL_SUN_MOON_COLOR = 19;
const S32 WL_AMBIENT = 20;
const S32 WL_EAST_ANGLE = 21;
const S32 WL_SUN_GLOW_FOCUS = 22;
const S32 WL_SUN_GLOW_SIZE = 23;
const S32 WL_SCENE_GAMMA = 24;
const S32 WL_STAR_BRIGHTNESS = 25;
const S32 WL_CLOUD_COLOR = 26;
const S32 WL_CLOUD_XY_DENSITY = 27;
const S32 WL_CLOUD_COVERAGE = 28;
const S32 WL_CLOUD_SCALE = 29;
const S32 WL_CLOUD_DETAIL_XY_DENSITY = 30;
const S32 WL_CLOUD_SCROLL_X = 31;
const S32 WL_CLOUD_SCROLL_Y = 32;
const S32 WL_CLOUD_SCROLL_Y_LOCK = 33;
const S32 WL_CLOUD_SCROLL_X_LOCK = 34;
const S32 WL_DRAW_CLASSIC_CLOUDS = 35;
const S32 WL_SUN_MOON_POSITION = 36;
// Aurora-Sim Constants (\Aurora\AuroraDotNetEngine\APIs\AA_Constants.cs) -->
const S32 BOT_FOLLOW_FLAG_NONE = 0;
const S32 BOT_FOLLOW_FLAG_INDEFINITELY = 1;
const S32 BOT_FOLLOW_WALK = 0;
const S32 BOT_FOLLOW_RUN = 1;
const S32 BOT_FOLLOW_FLY = 2;
const S32 BOT_FOLLOW_TELEPORT = 3;
const S32 BOT_FOLLOW_WAIT = 4;
const S32 BOT_FOLLOW_TRIGGER_HERE_EVENT = 1;
const S32 BOT_FOLLOW_FLAG_FORCEDIRECTPATH = 4;
// string constants from Aurora-Sim
char const* const ENABLE_GRAVITY = "enable_gravity";
char const* const GRAVITY_FORCE_X = "gravity_force_x";
char const* const GRAVITY_FORCE_Y = "gravity_force_y";
char const* const GRAVITY_FORCE_Z = "gravity_force_z";
char const* const ADD_GRAVITY_POINT = "add_gravity_point";
char const* const ADD_GRAVITY_FORCE = "add_gravity_force";
char const* const START_TIME_REVERSAL_SAVING = "start_time_reversal_saving";
char const* const STOP_TIME_REVERSAL_SAVING = "stop_time_reversal_saving";
char const* const START_TIME_REVERSAL = "start_time_reversal";
char const* const STOP_TIME_REVERSAL = "stop_time_reversal";
#endif

View File

@@ -725,6 +725,29 @@ void LLImageRaw::compositeUnscaled4onto3( LLImageRaw* src )
}
}
void LLImageRaw::copyUnscaledAlphaMask( LLImageRaw* src, const LLColor4U& fill)
{
LLImageRaw* dst = this; // Just for clarity.
llassert( 1 == src->getComponents() );
llassert( 4 == dst->getComponents() );
llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
S32 pixels = getWidth() * getHeight();
U8* src_data = src->getData();
U8* dst_data = dst->getData();
for ( S32 i = 0; i < pixels; i++ )
{
dst_data[0] = fill.mV[0];
dst_data[1] = fill.mV[1];
dst_data[2] = fill.mV[2];
dst_data[3] = src_data[0];
src_data += 1;
dst_data += 4;
}
}
// Fill the buffer with a constant color
void LLImageRaw::fill( const LLColor4U& color )
{

View File

@@ -219,6 +219,11 @@ public:
// Src and dst are same size. Src has 3 components. Dst has 4 components.
void copyUnscaled3onto4( LLImageRaw* src );
// Src and dst are same size. Src has 1 component. Dst has 4 components.
// Alpha component is set to source alpha mask component.
// RGB components are set to fill color.
void copyUnscaledAlphaMask( LLImageRaw* src, const LLColor4U& fill);
// Src and dst can be any size. Src and dst have same number of components.
void copyScaled( LLImageRaw* src );

View File

@@ -342,15 +342,6 @@ void LLInventoryItem::copyItem(const LLInventoryItem* other)
mCreationDate = other->mCreationDate;
}
// As a constructor alternative, the clone() method works like a
// copy constructor, but gens a new UUID.
void LLInventoryItem::cloneItem(LLPointer<LLInventoryItem>& newitem) const
{
newitem = new LLInventoryItem;
newitem->copyItem(this);
newitem->mUUID.generate();
}
// If this is a linked item, then the UUID of the base object is
// this item's assetID.
// virtual
@@ -390,6 +381,11 @@ const std::string& LLInventoryItem::getDescription() const
return mDescription;
}
const std::string& LLInventoryItem::getActualDescription() const
{
return mDescription;
}
time_t LLInventoryItem::getCreationDate() const
{
return mCreationDate;
@@ -1632,36 +1628,6 @@ LLSD ll_create_sd_from_inventory_item(LLPointer<LLInventoryItem> item)
return rv;
}
LLPointer<LLInventoryItem> ll_create_item_from_sd(const LLSD& sd_item)
{
LLPointer<LLInventoryItem> rv = new LLInventoryItem;
rv->setUUID(sd_item[INV_ITEM_ID_LABEL].asUUID());
rv->setParent(sd_item[INV_PARENT_ID_LABEL].asUUID());
rv->rename(sd_item[INV_NAME_LABEL].asString());
rv->setType(
LLAssetType::lookup(sd_item[INV_ASSET_TYPE_LABEL].asString()));
if (sd_item.has("shadow_id"))
{
LLUUID asset_id = sd_item["shadow_id"];
LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
cipher.decrypt(asset_id.mData, UUID_BYTES);
rv->setAssetUUID(asset_id);
}
if (sd_item.has(INV_ASSET_ID_LABEL))
{
rv->setAssetUUID(sd_item[INV_ASSET_ID_LABEL].asUUID());
}
rv->setDescription(sd_item[INV_DESC_LABEL].asString());
rv->setSaleInfo(ll_sale_info_from_sd(sd_item[INV_SALE_INFO_LABEL]));
rv->setPermissions(ll_permissions_from_sd(sd_item[INV_PERMISSIONS_LABEL]));
rv->setInventoryType(
LLInventoryType::lookup(
sd_item[INV_INVENTORY_TYPE_LABEL].asString()));
rv->setFlags((U32)(sd_item[INV_FLAGS_LABEL].asInteger()));
rv->setCreationDate(sd_item[INV_CREATION_DATE_LABEL].asInteger());
return rv;
}
LLSD ll_create_sd_from_inventory_category(LLPointer<LLInventoryCategory> cat)
{
LLSD rv;

View File

@@ -54,7 +54,6 @@ public:
// Initialization
//--------------------------------------------------------------------
public:
MEM_TYPE_NEW(LLMemType::MTYPE_INVENTORY);
LLInventoryObject();
LLInventoryObject(const LLUUID& uuid,
const LLUUID& parent_uuid,
@@ -129,7 +128,6 @@ public:
// Initialization
//--------------------------------------------------------------------
public:
MEM_TYPE_NEW(LLMemType::MTYPE_INVENTORY);
LLInventoryItem(const LLUUID& uuid,
const LLUUID& parent_uuid,
const LLPermissions& permissions,
@@ -148,11 +146,6 @@ public:
LLInventoryItem(const LLInventoryItem* other);
virtual void copyItem(const LLInventoryItem* other); // LLRefCount requires custom copy
void generateUUID() { mUUID.generate(); }
// As a constructor alternative, the clone() method works like a
// copy constructor, but gens a new UUID.
// It is up to the caller to delete (unref) the item.
virtual void cloneItem(LLPointer<LLInventoryItem>& newitem) const;
protected:
~LLInventoryItem(); // ref counted
@@ -165,6 +158,7 @@ public:
virtual const LLUUID& getCreatorUUID() const;
virtual const LLUUID& getAssetUUID() const;
virtual const std::string& getDescription() const;
virtual const std::string& getActualDescription() const; // Does not follow links
virtual const LLSaleInfo& getSaleInfo() const;
virtual LLInventoryType::EType getInventoryType() const;
virtual U32 getFlags() const;
@@ -247,7 +241,6 @@ public:
// Initialization
//--------------------------------------------------------------------
public:
MEM_TYPE_NEW(LLMemType::MTYPE_INVENTORY);
LLInventoryCategory(const LLUUID& uuid, const LLUUID& parent_uuid,
LLFolderType::EType preferred_type,
const std::string& name);
@@ -297,7 +290,6 @@ protected:
// item, appropriate for serialization.
//-----------------------------------------------------------------------------
LLSD ll_create_sd_from_inventory_item(LLPointer<LLInventoryItem> item);
LLPointer<LLInventoryItem> ll_create_item_from_sd(const LLSD& sd_item);
LLSD ll_create_sd_from_inventory_category(LLPointer<LLInventoryCategory> cat);
LLPointer<LLInventoryCategory> ll_create_category_from_sd(const LLSD& sd_cat);

View File

@@ -206,7 +206,7 @@ void LLParcel::init(const LLUUID &owner_id,
mAABBMin.setVec(SOME_BIG_NUMBER, SOME_BIG_NUMBER, SOME_BIG_NUMBER);
mAABBMax.setVec(SOME_BIG_NEG_NUMBER, SOME_BIG_NEG_NUMBER, SOME_BIG_NEG_NUMBER);
mLocalID = 0;
mLocalID = INVALID_PARCEL_ID;
//mSimWidePrimCorrection = 0;
setMaxPrimCapacity((S32)(sim_object_limit * area / (F32)(REGION_WIDTH_METERS * REGION_WIDTH_METERS)));

View File

@@ -103,6 +103,7 @@ const U32 RT_OTHER = 0x1 << 3;
const U32 RT_LIST = 0x1 << 4;
const U32 RT_SELL = 0x1 << 5;
const S32 INVALID_PARCEL_ID = -1;
// Timeouts for parcels
// default is 21 days * 24h/d * 60m/h * 60s/m *1000000 usec/s = 1814400000000

View File

@@ -745,7 +745,7 @@ public:
}
//node is now root
llwarns << "!!! OCTREE REMOVING FACE BY ADDRESS, SEVERE PERFORMANCE PENALTY |||" << llendl;
llwarns << "!!! OCTREE REMOVING ELEMENT BY ADDRESS, SEVERE PERFORMANCE PENALTY |||" << llendl;
node->removeByAddress(data);
llassert(data->getBinIndex() == -1);
return true;

View File

@@ -4830,237 +4830,6 @@ BOOL equalTriangle(const S32 *a, const S32 *b)
return FALSE;
}
BOOL LLVolume::cleanupTriangleData( const S32 num_input_vertices,
const std::vector<Point>& input_vertices,
const S32 num_input_triangles,
S32 *input_triangles,
S32 &num_output_vertices,
LLVector3 **output_vertices,
S32 &num_output_triangles,
S32 **output_triangles)
{
LLMemType m1(LLMemType::MTYPE_VOLUME);
/* Testing: avoid any cleanup
static BOOL skip_cleanup = TRUE;
if ( skip_cleanup )
{
num_output_vertices = num_input_vertices;
num_output_triangles = num_input_triangles;
*output_vertices = new LLVector3[num_input_vertices];
for (S32 index = 0; index < num_input_vertices; index++)
{
(*output_vertices)[index] = input_vertices[index].mPos;
}
*output_triangles = new S32[num_input_triangles*3];
memcpy(*output_triangles, input_triangles, 3*num_input_triangles*sizeof(S32)); // Flawfinder: ignore
return TRUE;
}
*/
// Here's how we do this:
// Create a structure which contains the original vertex index and the
// LLVector3 data.
// "Sort" the data by the vectors
// Create an array the size of the old vertex list, with a mapping of
// old indices to new indices.
// Go through triangles, shift so the lowest index is first
// Sort triangles by first index
// Remove duplicate triangles
// Allocate and pack new triangle data.
//LLTimer cleanupTimer;
//llinfos << "In vertices: " << num_input_vertices << llendl;
//llinfos << "In triangles: " << num_input_triangles << llendl;
S32 i;
typedef std::multiset<LLVertexIndexPair*, lessVertex> vertex_set_t;
vertex_set_t vertex_list;
LLVertexIndexPair *pairp = NULL;
for (i = 0; i < num_input_vertices; i++)
{
LLVertexIndexPair *new_pairp = new LLVertexIndexPair(input_vertices[i].mPos, i);
vertex_list.insert(new_pairp);
}
// Generate the vertex mapping and the list of vertices without
// duplicates. This will crash if there are no vertices.
llassert(num_input_vertices > 0); // check for no vertices!
S32 *vertex_mapping = new S32[num_input_vertices];
LLVector3 *new_vertices = new LLVector3[num_input_vertices];
LLVertexIndexPair *prev_pairp = NULL;
S32 new_num_vertices;
new_num_vertices = 0;
for (vertex_set_t::iterator iter = vertex_list.begin(),
end = vertex_list.end();
iter != end; iter++)
{
pairp = *iter;
if (!prev_pairp || ((pairp->mVertex - prev_pairp->mVertex).magVecSquared() >= VERTEX_SLOP_SQRD))
{
new_vertices[new_num_vertices] = pairp->mVertex;
//llinfos << "Added vertex " << new_num_vertices << " : " << pairp->mVertex << llendl;
new_num_vertices++;
// Update the previous
prev_pairp = pairp;
}
else
{
//llinfos << "Removed duplicate vertex " << pairp->mVertex << ", distance magVecSquared() is " << (pairp->mVertex - prev_pairp->mVertex).magVecSquared() << llendl;
}
vertex_mapping[pairp->mIndex] = new_num_vertices - 1;
}
// Iterate through triangles and remove degenerates, re-ordering vertices
// along the way.
S32 *new_triangles = new S32[num_input_triangles * 3];
S32 new_num_triangles = 0;
for (i = 0; i < num_input_triangles; i++)
{
S32 v1 = i*3;
S32 v2 = v1 + 1;
S32 v3 = v1 + 2;
//llinfos << "Checking triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
input_triangles[v1] = vertex_mapping[input_triangles[v1]];
input_triangles[v2] = vertex_mapping[input_triangles[v2]];
input_triangles[v3] = vertex_mapping[input_triangles[v3]];
if ((input_triangles[v1] == input_triangles[v2])
|| (input_triangles[v1] == input_triangles[v3])
|| (input_triangles[v2] == input_triangles[v3]))
{
//llinfos << "Removing degenerate triangle " << input_triangles[v1] << ":" << input_triangles[v2] << ":" << input_triangles[v3] << llendl;
// Degenerate triangle, skip
continue;
}
if (input_triangles[v1] < input_triangles[v2])
{
if (input_triangles[v1] < input_triangles[v3])
{
// (0 < 1) && (0 < 2)
new_triangles[new_num_triangles*3] = input_triangles[v1];
new_triangles[new_num_triangles*3+1] = input_triangles[v2];
new_triangles[new_num_triangles*3+2] = input_triangles[v3];
}
else
{
// (0 < 1) && (2 < 0)
new_triangles[new_num_triangles*3] = input_triangles[v3];
new_triangles[new_num_triangles*3+1] = input_triangles[v1];
new_triangles[new_num_triangles*3+2] = input_triangles[v2];
}
}
else if (input_triangles[v2] < input_triangles[v3])
{
// (1 < 0) && (1 < 2)
new_triangles[new_num_triangles*3] = input_triangles[v2];
new_triangles[new_num_triangles*3+1] = input_triangles[v3];
new_triangles[new_num_triangles*3+2] = input_triangles[v1];
}
else
{
// (1 < 0) && (2 < 1)
new_triangles[new_num_triangles*3] = input_triangles[v3];
new_triangles[new_num_triangles*3+1] = input_triangles[v1];
new_triangles[new_num_triangles*3+2] = input_triangles[v2];
}
new_num_triangles++;
}
if (new_num_triangles == 0)
{
llwarns << "Created volume object with 0 faces." << llendl;
delete[] new_triangles;
delete[] vertex_mapping;
delete[] new_vertices;
return FALSE;
}
typedef std::set<S32*, lessTriangle> triangle_set_t;
triangle_set_t triangle_list;
for (i = 0; i < new_num_triangles; i++)
{
triangle_list.insert(&new_triangles[i*3]);
}
// Sort through the triangle list, and delete duplicates
S32 *prevp = NULL;
S32 *curp = NULL;
S32 *sorted_tris = new S32[new_num_triangles*3];
S32 cur_tri = 0;
for (triangle_set_t::iterator iter = triangle_list.begin(),
end = triangle_list.end();
iter != end; iter++)
{
curp = *iter;
if (!prevp || !equalTriangle(prevp, curp))
{
//llinfos << "Added triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
sorted_tris[cur_tri*3] = *curp;
sorted_tris[cur_tri*3+1] = *(curp+1);
sorted_tris[cur_tri*3+2] = *(curp+2);
cur_tri++;
prevp = curp;
}
else
{
//llinfos << "Skipped triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
}
}
*output_vertices = new LLVector3[new_num_vertices];
num_output_vertices = new_num_vertices;
for (i = 0; i < new_num_vertices; i++)
{
(*output_vertices)[i] = new_vertices[i];
}
*output_triangles = new S32[cur_tri*3];
num_output_triangles = cur_tri;
memcpy(*output_triangles, sorted_tris, 3*cur_tri*sizeof(S32)); /* Flawfinder: ignore */
/*
llinfos << "Out vertices: " << num_output_vertices << llendl;
llinfos << "Out triangles: " << num_output_triangles << llendl;
for (i = 0; i < num_output_vertices; i++)
{
llinfos << i << ":" << (*output_vertices)[i] << llendl;
}
for (i = 0; i < num_output_triangles; i++)
{
llinfos << i << ":" << (*output_triangles)[i*3] << ":" << (*output_triangles)[i*3+1] << ":" << (*output_triangles)[i*3+2] << llendl;
}
*/
//llinfos << "Out vertices: " << num_output_vertices << llendl;
//llinfos << "Out triangles: " << num_output_triangles << llendl;
delete[] vertex_mapping;
vertex_mapping = NULL;
delete[] new_vertices;
new_vertices = NULL;
delete[] new_triangles;
new_triangles = NULL;
delete[] sorted_tris;
sorted_tris = NULL;
triangle_list.clear();
std::for_each(vertex_list.begin(), vertex_list.end(), DeletePointer());
vertex_list.clear();
return TRUE;
}
BOOL LLVolumeParams::importFile(LLFILE *fp)
{
LLMemType m1(LLMemType::MTYPE_VOLUME);
@@ -6385,12 +6154,7 @@ BOOL LLVolumeFace::createUnCutCubeCap(LLVolume* volume, BOOL partial_build)
S32 max_t = volume->getPath().mPath.size();
// S32 i;
S32 num_vertices = 0, num_indices = 0;
S32 grid_size = (profile.size()-1)/4;
S32 quad_count = (grid_size * grid_size);
num_vertices = (grid_size+1)*(grid_size+1);
num_indices = quad_count * 4;
LLVector4a& min = mExtents[0];
LLVector4a& max = mExtents[1];

View File

@@ -1025,17 +1025,6 @@ public:
LLVector3* normal = NULL,
LLVector3* bi_normal = NULL);
// The following cleans up vertices and triangles,
// getting rid of degenerate triangles and duplicate vertices,
// and allocates new arrays with the clean data.
static BOOL cleanupTriangleData( const S32 num_input_vertices,
const std::vector<Point> &input_vertices,
const S32 num_input_triangles,
S32 *input_triangles,
S32 &num_output_vertices,
LLVector3 **output_vertices,
S32 &num_output_triangles,
S32 **output_triangles);
LLFaceID generateFaceMask();
BOOL isFaceMaskValid(LLFaceID face_mask);

View File

@@ -824,7 +824,9 @@ AIHTTPTimeoutPolicy const* AIHTTPTimeoutPolicy::getTimeoutPolicyByName(std::stri
// Policy name Policy
P(accountingCostResponder);
P(agentStateResponder);
P(appearanceChangeMetricsResponder);
P(assetUploadResponder);
P(assetReportHandler);
P(asyncConsoleResponder);
P(avatarPickerResponder);
P(authHandler);

View File

@@ -886,8 +886,17 @@ void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user
return;
}
std::string expanded_filename = gDirUtilp->getExpandedFilename( local_path, local_filename );
// If we want to use a special path (e.g. LL_PATH_CACHE), we want to make sure we create the
// proper expanded filename.
std::string expanded_filename;
if (local_path != LL_PATH_NONE)
{
expanded_filename = gDirUtilp->getExpandedFilename( local_path, local_filename );
}
else
{
expanded_filename = local_filename;
}
llinfos << "starting file transfer: " << expanded_filename << " to " << mesgsys->getSender() << llendl;
BOOL delete_local_on_completion = FALSE;

View File

@@ -79,6 +79,15 @@ public:
MAX_GL_IMAGE_CATEGORY
};
typedef enum
{
DELETED = 0, //removed from memory
DELETION_CANDIDATE, //ready to be removed from memory
INACTIVE, //not be used for the last certain period (i.e., 30 seconds).
ACTIVE, //just being used, can become inactive if not being used for a certain time (10 seconds).
NO_DELETE = 99 //stay in memory, can not be removed.
} LLGLTextureState;
static S32 getTotalNumOfCategories() ;
static S32 getIndexFromCategory(S32 category) ;
static S32 getCategoryFromIndex(S32 index) ;
@@ -143,6 +152,8 @@ public:
U32 getTexelsInGLTexture() const ;
BOOL isGLTextureCreated() const ;
S32 getDiscardLevelInAtlas() const ;*/
LLGLTextureState getTextureState() const { return mTextureState; }
//---------------------------------------------------------------------------------------------
//end of functions to access LLImageGL
//---------------------------------------------------------------------------------------------
@@ -179,14 +190,6 @@ protected:
S8 mDontDiscard; // Keep full res version of this image (for UI, etc)
protected:
typedef enum
{
DELETED = 0, //removed from memory
DELETION_CANDIDATE, //ready to be removed from memory
INACTIVE, //not be used for the last certain period (i.e., 30 seconds).
ACTIVE, //just being used, can become inactive if not being used for a certain time (10 seconds).
NO_DELETE = 99 //stay in memory, can not be removed.
} LLGLTextureState;
LLGLTextureState mTextureState ;

View File

@@ -467,6 +467,7 @@ set(viewer_SOURCE_FILES
llvectorperfoptions.cpp
llvelocitybar.cpp
llviewchildren.cpp
llviewerassetstats.cpp
llviewerassetstorage.cpp
llviewerassettype.cpp
llvieweraudio.cpp
@@ -917,6 +918,7 @@ set(viewer_HEADER_FILES
llscrollingpanelparam.h
llscrollingpanelparambase.h
llselectmgr.h
llsimplestat.h
llsky.h
llspatialpartition.h
llsprite.h
@@ -972,6 +974,7 @@ set(viewer_HEADER_FILES
llvectorperfoptions.h
llvelocitybar.h
llviewchildren.h
llviewerassetstats.h
llviewerassetstorage.h
llviewerassettype.h
llvieweraudio.h

View File

@@ -26,22 +26,26 @@
ATTRIBUTE vec4 weight4;
uniform mat4 matrixPalette[32];
uniform mat4 matrixPalette[64];
mat4 getObjectSkinnedTransform()
{
int i;
float w0 = fract(weight4.x);
float w1 = fract(weight4.y);
float w2 = fract(weight4.z);
float w3 = fract(weight4.w);
vec4 w = fract(weight4);
vec4 index = floor(weight4);
int i0 = int(floor(weight4.x));
int i1 = int(floor(weight4.y));
int i2 = int(floor(weight4.z));
int i3 = int(floor(weight4.w));
float scale = 1.0/(w.x+w.y+w.z+w.w);
w *= scale;
mat4 mat = matrixPalette[int(index.x)]*w.x;
mat += matrixPalette[int(index.y)]*w.y;
mat += matrixPalette[int(index.z)]*w.z;
mat += matrixPalette[int(index.w)]*w.w;
//float scale = 1.0/(w.x+w.y+w.z+w.w);
//w *= scale;
mat4 mat = matrixPalette[i0]*w0;
mat += matrixPalette[i1]*w1;
mat += matrixPalette[i2]*w2;
mat += matrixPalette[i3]*w3;
return mat;
}

View File

@@ -11843,6 +11843,17 @@
value_max="1">
<param_driver />
</param>
<param
id="11000"
group="0"
name="AppearanceMessage_Version"
label="AppearanceMessage Version"
value_default="0"
value_min="0"
value_max="255">
<param_driver />
</param>
</driver_parameters>
<morph_masks>

View File

@@ -34,8 +34,8 @@
#include "llagentcamera.h"
#include "llagentwearables.h"
#include "llagentui.h"
#include "llanimationstates.h"
#include "llappearancemgr.h"
#include "llanimationstates.h"
#include "llcallingcard.h"
#include "llcapabilitylistener.h"
#include "llconsole.h"
@@ -780,6 +780,7 @@ void LLAgent::handleServerBakeRegionTransition(const LLUUID& region_id)
!gAgentAvatarp->isUsingServerBakes() &&
(mRegionp->getCentralBakeVersion()>0))
{
llinfos << "update requested due to region transition" << llendl;
LLAppearanceMgr::instance().requestServerAppearanceUpdate();
}
}
@@ -852,6 +853,9 @@ void LLAgent::setRegion(LLViewerRegion *regionp)
// Update all of the regions.
LLWorld::getInstance()->updateAgentOffset(mAgentOriginGlobal);
}
// Pass new region along to metrics components that care about this level of detail.
LLAppViewer::metricsUpdateRegion(regionp->getHandle());
}
mRegionp = regionp;
@@ -3717,6 +3721,10 @@ void LLAgent::processControlRelease(LLMessageSystem *msg, void **)
void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data)
{
gAgentQueryManager.mNumPendingQueries--;
if (gAgentQueryManager.mNumPendingQueries == 0)
{
selfStopPhase("fetch_texture_cache_entries");
}
if (!isAgentAvatarValid() || gAgentAvatarp->isDead())
{
@@ -4419,24 +4427,92 @@ void LLAgent::requestLeaveGodMode()
sendReliableMessage();
}
extern void dump_visual_param(apr_file_t* file, LLVisualParam* viewer_param, F32 value);
extern std::string get_sequential_numbered_file_name(const std::string& prefix,
const std::string& suffix);
// For debugging, trace agent state at times appearance message are sent out.
void LLAgent::dumpSentAppearance(const std::string& dump_prefix)
{
std::string outfilename = get_sequential_numbered_file_name(dump_prefix,".xml");
LLAPRFile outfile;
std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfilename);
outfile.open(fullpath, LL_APR_WB );
apr_file_t* file = outfile.getFileHandle();
if (!file)
{
return;
}
else
{
LL_DEBUGS("Avatar") << "dumping sent appearance message to " << fullpath << llendl;
}
LLVisualParam* appearance_version_param = gAgentAvatarp->getVisualParam(11000);
if (appearance_version_param)
{
F32 value = appearance_version_param->getWeight();
dump_visual_param(file, appearance_version_param, value);
}
for (LLAvatarAppearanceDictionary::Textures::const_iterator iter = LLAvatarAppearanceDictionary::getInstance()->getTextures().begin();
iter != LLAvatarAppearanceDictionary::getInstance()->getTextures().end();
++iter)
{
const ETextureIndex index = iter->first;
const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = iter->second;
if (texture_dict->mIsBakedTexture)
{
LLTextureEntry* entry = gAgentAvatarp->getTE((U8) index);
const LLUUID& uuid = entry->getID();
apr_file_printf( file, "\t\t<texture te=\"%i\" uuid=\"%s\"/>\n", index, uuid.asString().c_str());
}
}
}
//-----------------------------------------------------------------------------
// sendAgentSetAppearance()
//-----------------------------------------------------------------------------
void LLAgent::sendAgentSetAppearance()
{
// FIXME DRANO - problems around new-style appearance in an old-style region.
// - does this get called?
// - need to change mUseServerBakes->FALSE in that case
// - need to call processAvatarAppearance as if server had returned this result?
// gAgentAvatarp->mUseServerBakes = FALSE;
if (gAgentQueryManager.mNumPendingQueries > 0)
{
return;
}
if (!gAgentWearables.changeInProgress())
{
// Change is fully resolved, can close some open phases.
gAgentAvatarp->stopPhase("process_initial_wearables_update");
gAgentAvatarp->stopPhase("wear_inventory_category");
}
if (!isAgentAvatarValid() || (getRegion() && getRegion()->getCentralBakeVersion())) return;
// At this point we have a complete appearance to send and are in a non-baking region.
// DRANO FIXME
//gAgentAvatarp->setIsUsingServerBakes(FALSE);
S32 sb_count, host_count, both_count, neither_count;
gAgentAvatarp->bakedTextureOriginCounts(sb_count, host_count, both_count, neither_count);
if (both_count != 0 || neither_count != 0)
{
llwarns << "bad bake texture state " << sb_count << "," << host_count << "," << both_count << "," << neither_count << llendl;
}
if (sb_count != 0 && host_count == 0)
{
gAgentAvatarp->setIsUsingServerBakes(true);
}
else if (sb_count == 0 && host_count != 0)
{
gAgentAvatarp->setIsUsingServerBakes(false);
}
else if (sb_count + host_count > 0)
{
llwarns << "unclear baked texture state, not sending appearance" << llendl;
return;
}
LL_INFOS("Avatar") << gAgentAvatarp->avString() << "TAT: Sent AgentSetAppearance: " << gAgentAvatarp->getBakedStatusForPrintout() << LL_ENDL;
//dumpAvatarTEs( "sendAgentSetAppearance()" );
@@ -4488,14 +4564,25 @@ void LLAgent::sendAgentSetAppearance()
// IMG_DEFAULT_AVATAR means not baked. 0 index should be ignored for baked textures
if (!gAgentAvatarp->isTextureDefined(texture_index, 0))
{
LL_DEBUGS("Avatar") << "texture not current for baked " << (S32)baked_index << " local " << (S32)texture_index << llendl;
textures_current = FALSE;
break;
}
}
// only update cache entries if we have all our baked textures
// FIXME DRANO need additional check for not in appearance editing
// mode, if still using local composites need to set using local
// composites to false, and update mesh textures.
if (textures_current)
{
bool enable_verbose_dumps = gSavedSettings.getBOOL("DebugAvatarAppearanceMessage");
std::string dump_prefix = gAgentAvatarp->getFullname() + "_sent_appearance";
if (enable_verbose_dumps)
{
dumpSentAppearance(dump_prefix);
}
LL_INFOS("Avatar") << gAgentAvatarp->avString() << "TAT: Sending cached texture data" << LL_ENDL;
for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
{

View File

@@ -861,8 +861,9 @@ private:
// Send
//--------------------------------------------------------------------
public:
void sendMessage(); // Send message to this agent's region.
void sendMessage(); // Send message to this agent's region
void sendReliableMessage();
void dumpSentAppearance(const std::string& dump_prefix);
void sendAgentSetAppearance();
void sendAgentDataUpdateRequest();
void sendAgentUserInfoRequest();

View File

@@ -1416,7 +1416,7 @@ void LLAgentCamera::updateCamera()
// llinfos << "Current FOV Zoom: " << mCameraCurrentFOVZoomFactor << " Target FOV Zoom: " << mCameraFOVZoomFactor << " Object penetration: " << mFocusObjectDist << llendl;
F32 ui_offset = 0.f;
if( LLFloaterCustomize::instanceExists() /*CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode*/ )
if( CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode )
{
ui_offset = calcCustomizeAvatarUIOffset( camera_pos_global );
}
@@ -1543,7 +1543,7 @@ F32 LLAgentCamera::calcCustomizeAvatarUIOffset( const LLVector3d& camera_pos_glo
{
F32 ui_offset = 0.f;
if( LLFloaterCustomize::instanceExists() )
if( LLFloaterCustomize::instanceExists() && CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode )
{
const LLRect& rect = LLFloaterCustomize::getInstance()->getRect();
@@ -2603,7 +2603,7 @@ void LLAgentCamera::setCameraPosAndFocusGlobal(const LLVector3d& camera_pos, con
{
startCameraAnimation();
if( LLFloaterCustomize::instanceExists()/*CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode*/ )
if( CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode )
{
// Compensate for the fact that the camera has already been offset to make room for LLFloaterCustomize.
mAnimationCameraStartGlobal -= LLVector3d(LLViewerCamera::getInstance()->getLeftAxis() * calcCustomizeAvatarUIOffset( mAnimationCameraStartGlobal ));

View File

@@ -226,14 +226,14 @@ LLAgentWearables::sendAgentWearablesUpdateCallback::~sendAgentWearablesUpdateCal
* @param todo Bitmask of actions to take on completion.
*/
LLAgentWearables::addWearableToAgentInventoryCallback::addWearableToAgentInventoryCallback(
LLPointer<LLRefCount> cb, LLWearableType::EType type, U32 index, LLViewerWearable* wearable, U32 todo) :
LLPointer<LLRefCount> cb, LLWearableType::EType type, U32 index, LLViewerWearable* wearable, U32 todo, const std::string description) :
mType(type),
mIndex(index),
mWearable(wearable),
mTodo(todo),
mCB(cb)
mCB(cb),
mDescription(description)
{
//llassert_always(index == 0);
llinfos << "constructor" << llendl;
}
@@ -272,7 +272,7 @@ void LLAgentWearables::addWearableToAgentInventoryCallback::fire(const LLUUID& i
}
if (mTodo & CALL_WEARITEM)
{
LLAppearanceMgr::instance().addCOFItemLink(inv_item, true);
LLAppearanceMgr::instance().addCOFItemLink(inv_item, true, NULL, mDescription);
}
}
@@ -473,6 +473,7 @@ void LLAgentWearables::saveWearable(const LLWearableType::EType type, const U32
LLViewerWearable* LLAgentWearables::saveWearableAs(const LLWearableType::EType type,
const U32 index,
const std::string& new_name,
const std::string& description,
BOOL save_in_lost_and_found)
{
if (!isWearableCopyable(type, index))
@@ -504,7 +505,9 @@ LLViewerWearable* LLAgentWearables::saveWearableAs(const LLWearableType::EType t
type,
index,
new_wearable,
addWearableToAgentInventoryCallback::CALL_WEARITEM);
addWearableToAgentInventoryCallback::CALL_WEARITEM,
description
);
LLUUID category_id;
if (save_in_lost_and_found)
{
@@ -850,6 +853,8 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs
if (isAgentAvatarValid())
{
//gAgentAvatarp->clearPhases(); // reset phase timers for outfit loading.
gAgentAvatarp->startPhase("process_initial_wearables_update");
gAgentAvatarp->outputRezTiming("Received initial wearables update");
}
@@ -1646,6 +1651,7 @@ void LLAgentWearables::queryWearableCache()
{
if (isAgentAvatarValid())
{
selfStartPhase("fetch_texture_cache_entries");
gAgentAvatarp->outputRezTiming("Fetching textures from cache");
}

View File

@@ -191,7 +191,7 @@ private:
// Save Wearables
//--------------------------------------------------------------------
public:
LLViewerWearable* saveWearableAs(const LLWearableType::EType type, const U32 index, const std::string& new_name, BOOL save_in_lost_and_found);
LLViewerWearable* saveWearableAs(const LLWearableType::EType type, const U32 index, const std::string& new_name, const std::string& description, BOOL save_in_lost_and_found);
void saveWearable(const LLWearableType::EType type, const U32 index, BOOL send_update = TRUE,
const std::string new_name = "");
void saveAllWearables();
@@ -291,7 +291,8 @@ private:
LLWearableType::EType type,
U32 index,
LLViewerWearable* wearable,
U32 todo = CALL_NONE);
U32 todo = CALL_NONE,
const std::string description = "");
virtual void fire(const LLUUID& inv_item);
private:
LLWearableType::EType mType;
@@ -299,6 +300,7 @@ private:
LLViewerWearable* mWearable;
U32 mTodo;
LLPointer<LLRefCount> mCB;
std::string mDescription;
};
}; // LLAgentWearables

View File

@@ -86,6 +86,7 @@ LLInitialWearablesFetch::LLInitialWearablesFetch(const LLUUID& cof_id) :
{
if (isAgentAvatarValid())
{
gAgentAvatarp->startPhase("initial_wearables_fetch");
gAgentAvatarp->outputRezTiming("Initial wearables fetch started");
}
}
@@ -111,6 +112,7 @@ void LLInitialWearablesFetch::done()
// [/RLVa:KB]
if (isAgentAvatarValid())
{
gAgentAvatarp->stopPhase("initial_wearables_fetch");
gAgentAvatarp->outputRezTiming("Initial wearables fetch done");
}
}

View File

@@ -48,6 +48,7 @@
#include "llviewerregion.h"
#include "llviewerstats.h"
#include "llwearablelist.h"
#include "llsdutil.h"
#include "llinventorypanel.h"
#include "llfloatercustomize.h"
// [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a)
@@ -58,10 +59,8 @@
std::string self_av_string()
{
if(isAgentAvatarValid())
return gAgentAvatarp->avString();
else
return std::string();
// On logout gAgentAvatarp can already be invalid
return isAgentAvatarValid() ? gAgentAvatarp->avString() : "";
}
// RAII thingy to guarantee that a variable gets reset when the Setter
@@ -163,6 +162,16 @@ LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id, const std::string
}
}
// We want this to be much lower (e.g. 15.0 is usually fine), bumping
// up for now until we can diagnose some cases of very slow response
// to requests.
const F32 DEFAULT_RETRY_AFTER_INTERVAL = 300.0;
// Given the current back-end problems, retrying is causing too many
// duplicate items. Bump this back to 2 once they are resolved (or can
// leave at 0 if the operations become actually reliable).
const S32 DEFAULT_MAX_RETRIES = 0;
class LLCallAfterInventoryBatchMgr: public LLEventTimer
{
public:
@@ -170,8 +179,8 @@ public:
const std::string& phase_name,
nullary_func_t on_completion_func,
nullary_func_t on_failure_func = no_op,
F32 retry_after = 15.0,
S32 max_retries = 2
F32 retry_after = DEFAULT_RETRY_AFTER_INTERVAL,
S32 max_retries = DEFAULT_MAX_RETRIES
):
mDstCatID(dst_cat_id),
mTrackingPhase(phase_name),
@@ -183,11 +192,11 @@ public:
mFailCount(0),
mCompletionOrFailureCalled(false),
mRetryCount(0),
LLEventTimer(retry_after)
LLEventTimer(5.0)
{
if (!mTrackingPhase.empty())
{
//selfStartPhase(mTrackingPhase);
selfStartPhase(mTrackingPhase);
}
}
@@ -206,52 +215,38 @@ public:
// Request or re-request operation for specified item.
void addItem(const LLUUID& item_id)
{
LLUUID linked_id;
if (gInventory.getItem(item_id))
{
linked_id = gInventory.getItem(item_id)->getLinkedUUID();
}
else if (gInventory.getCategory(item_id))
{
linked_id = item_id;
}
else
{
llwarns << "no referent found for item_id " << item_id << llendl;
return;
}
LL_DEBUGS("Avatar") << "item_id " << item_id << " -> linked_id " << linked_id << llendl;
LL_DEBUGS("Avatar") << "item_id " << item_id << llendl;
if (ll_frand()<gSavedSettings.getF32("InventoryDebugSimulateOpFailureRate"))
if (!requestOperation(item_id))
{
// simulate server failure by not sending the request.
return;
}
if (!requestOperation(linked_id))
{
LL_DEBUGS("Avatar") << "item_id " << item_id << " linked_id " << linked_id << " not requested" << llendl;
LL_DEBUGS("Avatar") << "item_id " << item_id << " requestOperation false, skipping" << llendl;
return;
}
mPendingRequests++;
// On a re-request, this will reset the timer.
mWaitTimes[linked_id] = LLTimer();
if (mRetryCounts.find(linked_id) == mRetryCounts.end())
mWaitTimes[item_id] = LLTimer();
if (mRetryCounts.find(item_id) == mRetryCounts.end())
{
mRetryCounts[linked_id] = 0;
mRetryCounts[item_id] = 0;
}
else
{
mRetryCounts[linked_id]++;
mRetryCounts[item_id]++;
}
}
virtual bool requestOperation(const LLUUID& item_id) = 0;
void onOp(const LLUUID& src_id, const LLUUID& dst_id, LLTimer timestamp)
{
if (ll_frand() < gSavedSettings.getF32("InventoryDebugSimulateLateOpRate"))
{
llwarns << "Simulating late operation by punting handling to later" << llendl;
doAfterInterval(boost::bind(&LLCallAfterInventoryBatchMgr::onOp,this,src_id,dst_id,timestamp),
mRetryAfter);
return;
}
mPendingRequests--;
F32 elapsed = timestamp.getElapsedTimeF32();
LL_DEBUGS("Avatar") << "op done, src_id " << src_id << " dst_id " << dst_id << " after " << elapsed << " seconds" << llendl;
@@ -281,7 +276,7 @@ public:
reportStats();
if (!mTrackingPhase.empty())
{
//selfStopPhase(mTrackingPhase);
selfStopPhase(mTrackingPhase);
}
if (!mFailCount)
{
@@ -394,8 +389,8 @@ public:
const std::string& phase_name,
nullary_func_t on_completion_func,
nullary_func_t on_failure_func = no_op,
F32 retry_after = 15.0,
S32 max_retries = 2
F32 retry_after = DEFAULT_RETRY_AFTER_INTERVAL,
S32 max_retries = DEFAULT_MAX_RETRIES
):
LLCallAfterInventoryBatchMgr(dst_cat_id, phase_name, on_completion_func, on_failure_func, retry_after, max_retries)
{
@@ -407,6 +402,11 @@ public:
LLViewerInventoryItem *item = gInventory.getItem(item_id);
llassert(item);
LL_DEBUGS("Avatar") << "copying item " << item_id << llendl;
if (ll_frand() < gSavedSettings.getF32("InventoryDebugSimulateOpFailureRate"))
{
LL_DEBUGS("Avatar") << "simulating failure by not sending request for item " << item_id << llendl;
return true;
}
copy_inventory_item(
gAgent.getID(),
item->getPermissions().getOwner(),
@@ -427,8 +427,8 @@ public:
const std::string& phase_name,
nullary_func_t on_completion_func,
nullary_func_t on_failure_func = no_op,
F32 retry_after = 15.0,
S32 max_retries = 2
F32 retry_after = DEFAULT_RETRY_AFTER_INTERVAL,
S32 max_retries = DEFAULT_MAX_RETRIES
):
LLCallAfterInventoryBatchMgr(dst_cat_id, phase_name, on_completion_func, on_failure_func, retry_after, max_retries)
{
@@ -441,17 +441,27 @@ public:
LLViewerInventoryItem *item = gInventory.getItem(item_id);
if (item)
{
if (item->getParentUUID() == mDstCatID)
{
LL_DEBUGS("Avatar") << "item " << item_id << " name " << item->getName() << " is already a child of " << mDstCatID << llendl;
return false;
}
LL_DEBUGS("Avatar") << "linking item " << item_id << " name " << item->getName() << " to " << mDstCatID << llendl;
// create an inventory item link.
if (ll_frand() < gSavedSettings.getF32("InventoryDebugSimulateOpFailureRate"))
{
LL_DEBUGS("Avatar") << "simulating failure by not sending request for item " << item_id << llendl;
return true;
}
link_inventory_item(gAgent.getID(),
item_id,
item->getLinkedUUID(),
mDstCatID,
item->getName(),
item->LLInventoryItem::getDescription(),
item->getActualDescription(),
LLAssetType::AT_LINK,
new LLBoostFuncInventoryCallback(
boost::bind(&LLCallAfterInventoryBatchMgr::onOp,this,item_id,_1,LLTimer())));
request_sent = true;
return true;
}
else
{
@@ -459,7 +469,7 @@ public:
LLViewerInventoryCategory *catp = gInventory.getCategory(item_id);
if (!catp)
{
llwarns << "id not found as inventory item or category " << item_id << llendl;
llwarns << "link request failed, id not found as inventory item or category " << item_id << llendl;
return false;
}
const LLUUID cof = LLAppearanceMgr::instance().getCOF();
@@ -469,6 +479,11 @@ public:
if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
{
if (ll_frand() < gSavedSettings.getF32("InventoryDebugSimulateOpFailureRate"))
{
LL_DEBUGS("Avatar") << "simulating failure by not sending request for item " << item_id << llendl;
return true;
}
LL_DEBUGS("Avatar") << "linking folder " << item_id << " name " << catp->getName() << " to cof " << cof << llendl;
link_inventory_item(gAgent.getID(), item_id, cof, catp->getName(), "",
LLAssetType::AT_LINK_FOLDER,
@@ -488,7 +503,7 @@ LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit
mFireCount(0),
mUpdateBaseOrder(update_base_outfit_ordering)
{
//selfStartPhase("update_appearance_on_destroy");
selfStartPhase("update_appearance_on_destroy");
}
LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
@@ -498,7 +513,7 @@ LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
// speculative fix for MAINT-1150
LL_INFOS("Avatar") << self_av_string() << "done update appearance on destroy" << LL_ENDL;
//selfStopPhase("update_appearance_on_destroy");
selfStopPhase("update_appearance_on_destroy");
LLAppearanceMgr::instance().updateAppearanceFromCOF(mUpdateBaseOrder);
}
@@ -631,7 +646,7 @@ LLWearableHoldingPattern::~LLWearableHoldingPattern()
sActiveHoldingPatterns.erase(this);
if (isMostRecent())
{
//selfStopPhase("holding_pattern");
selfStopPhase("holding_pattern");
}
}
@@ -722,7 +737,7 @@ void LLWearableHoldingPattern::checkMissingWearables()
resetTime(60.0F);
//selfStartPhase("get_missing_wearables");
selfStartPhase("get_missing_wearables");
if (!pollMissingWearables())
{
doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this));
@@ -787,7 +802,7 @@ void LLWearableHoldingPattern::onAllComplete()
void LLWearableHoldingPattern::onFetchCompletion()
{
//selfStopPhase("get_wearables");
selfStopPhase("get_wearables");
if (!isMostRecent())
{
@@ -998,7 +1013,7 @@ bool LLWearableHoldingPattern::pollMissingWearables()
if (done)
{
//selfStopPhase("get_missing_wearables");
selfStopPhase("get_missing_wearables");
gAgentAvatarp->debugWearablesLoaded();
@@ -1228,6 +1243,21 @@ S32 LLAppearanceMgr::getCOFVersion() const
}
}
S32 LLAppearanceMgr::getLastUpdateRequestCOFVersion() const
{
return mLastUpdateRequestCOFVersion;
}
S32 LLAppearanceMgr::getLastAppearanceUpdateCOFVersion() const
{
return mLastAppearanceUpdateCOFVersion;
}
void LLAppearanceMgr::setLastAppearanceUpdateCOFVersion(S32 new_val)
{
mLastAppearanceUpdateCOFVersion = new_val;
}
const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink()
{
const LLUUID& current_outfit_cat = getCOF();
@@ -1522,7 +1552,8 @@ void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& ds
}
// Copy contents of src_id to dst_id.
void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id, LLPointer<LLInventoryCallback> cb)
void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id,
LLPointer<LLInventoryCallback> cb)
{
LLInventoryModel::cat_array_t* cats;
LLInventoryModel::item_array_t* items;
@@ -1548,7 +1579,7 @@ void LLAppearanceMgr::copyItems(const LLUUID& dst_id, LLInventoryModel::item_arr
item->getLinkedUUID(),
dst_id,
item->getName(),
item->LLInventoryItem::getDescription(),
item->getActualDescription(),
LLAssetType::AT_LINK, cb);
break;
}
@@ -1838,7 +1869,7 @@ void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid,
item->getLinkedUUID(),
cat_uuid,
item->getName(),
item->LLInventoryItem::getDescription(),
item->getActualDescription(),
LLAssetType::AT_LINK,
cb);
@@ -1981,34 +2012,23 @@ void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new,
// Create links to new COF contents.
LL_DEBUGS("Avatar") << self_av_string() << "creating LLCallAfterInventoryLinkMgr" << LL_ENDL;
LLInventoryModel::item_array_t all_items;
/*all_items += body_items;
all_items += wear_items;
all_items += obj_items;
all_items += gest_items;*/
// Will link all the above items.
bool update_base_outfit_ordering = !append;
LLCallAfterInventoryLinkMgr *link_waiter =
new LLCallAfterInventoryLinkMgr(body_items,cof,"update_appearance_on_destroy",
new LLCallAfterInventoryLinkMgr(all_items,cof,"update_appearance_on_destroy",
boost::bind(&LLAppearanceMgr::updateAppearanceFromCOF,
LLAppearanceMgr::getInstance(),
update_base_outfit_ordering));
// [SL:KB] - Checked: 2010-04-24 (RLVa-1.2.0f) | Added: RLVa-1.2.0f
if (!append)
{
// [/SL:KB]
#ifndef LL_RELEASE_FOR_DOWNLOAD
LL_DEBUGS("Avatar") << self_av_string() << "Linking wear items" << LL_ENDL;
#endif
link_waiter->addItems(wear_items);
#ifndef LL_RELEASE_FOR_DOWNLOAD
LL_DEBUGS("Avatar") << self_av_string() << "Linking obj items" << LL_ENDL;
#endif
link_waiter->addItems(obj_items);
#ifndef LL_RELEASE_FOR_DOWNLOAD
LL_DEBUGS("Avatar") << self_av_string() << "Linking gesture items" << LL_ENDL;
#endif
link_waiter->addItems(gest_items);
// [SL:KB] - Checked: 2010-04-24 (RLVa-1.2.0f) | Added: RLVa-1.2.0f
}
else
if(append)
{
// Synchronize COF
// -> it's possible that we don't link to any new items in which case 'link_waiter' fires when it goes out of scope below
@@ -2023,9 +2043,14 @@ void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new,
// [RLVa:KB] - Checked: Never | Added: RLVa-1.2.0b
if (!append && category.notNull())
// [/RLVa:KB]
// if (!append)
if (!append)
{
link_waiter->addItem(category);
link_waiter->addItems(body_items);
link_waiter->addItems(wear_items);
link_waiter->addItems(obj_items);
link_waiter->addItems(gest_items);
if(category.notNull())
link_waiter->addItem(category);
// Remove current COF contents. Have to do this after creating
// the link_waiter so links can be followed for any items that get
@@ -2174,7 +2199,7 @@ static void remove_non_link_items(LLInventoryModel::item_array_t &items)
}
//a predicate for sorting inventory items by actual descriptions
bool sort_by_description(const LLInventoryItem* item1, const LLInventoryItem* item2)
bool sort_by_actual_description(const LLInventoryItem* item1, const LLInventoryItem* item2)
{
if (!item1 || !item2)
{
@@ -2182,7 +2207,7 @@ bool sort_by_description(const LLInventoryItem* item1, const LLInventoryItem* it
return true;
}
return item1->LLInventoryItem::getDescription() < item2->LLInventoryItem::getDescription();
return item1->getActualDescription() < item2->getActualDescription();
}
void item_array_diff(LLInventoryModel::item_array_t& full_list,
@@ -2287,7 +2312,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
}
// DRANO really should wait for the appearance message to set this.
// verify that deleting this line doesn't break anything.
gAgentAvatarp->setIsUsingServerBakes(gAgent.getRegion() && gAgent.getRegion()->getCentralBakeVersion());
//gAgentAvatarp->setIsUsingServerBakes(gAgent.getRegion() && gAgent.getRegion()->getCentralBakeVersion());
//dumpCat(getCOF(),"COF, start");
@@ -2424,7 +2449,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
}
}
//selfStartPhase("get_wearables");
selfStartPhase("get_wearables");
for (LLWearableHoldingPattern::found_list_t::iterator it = holder->getFoundList().begin();
it != holder->getFoundList().end(); ++it)
@@ -2519,15 +2544,15 @@ void LLAppearanceMgr::wearInventoryCategory(LLInventoryCategory* category, bool
{
if(!category) return;
//selfClearPhases();
//selfStartPhase("wear_inventory_category");
selfClearPhases();
selfStartPhase("wear_inventory_category");
gAgentWearables.notifyLoadingStarted();
LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategory( " << category->getName()
<< " )" << LL_ENDL;
//selfStartPhase("wear_inventory_category_fetch");
selfStartPhase("wear_inventory_category_fetch");
callAfterCategoryFetch(category->getUUID(),boost::bind(&LLAppearanceMgr::wearCategoryFinal,
&LLAppearanceMgr::instance(),
category->getUUID(), copy, append));
@@ -2537,7 +2562,7 @@ void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool ap
{
LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL;
//selfStopPhase("wear_inventory_category_fetch");
selfStopPhase("wear_inventory_category_fetch");
// We now have an outfit ready to be copied to agent inventory. Do
// it, and wear that outfit normally.
@@ -2687,10 +2712,11 @@ bool areMatchingWearables(const LLViewerInventoryItem *a, const LLViewerInventor
class LLDeferredCOFLinkObserver: public LLInventoryObserver
{
public:
LLDeferredCOFLinkObserver(const LLUUID& item_id, bool do_update, LLPointer<LLInventoryCallback> cb = NULL):
LLDeferredCOFLinkObserver(const LLUUID& item_id, bool do_update, LLPointer<LLInventoryCallback> cb = NULL, std::string description = ""):
mItemID(item_id),
mDoUpdate(do_update),
mCallback(cb)
mCallback(cb),
mDescription(description)
{
}
@@ -2712,23 +2738,24 @@ public:
private:
const LLUUID mItemID;
bool mDoUpdate;
std::string mDescription;
LLPointer<LLInventoryCallback> mCallback;
};
// BAP - note that this runs asynchronously if the item is not already loaded from inventory.
// Dangerous if caller assumes link will exist after calling the function.
void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id, bool do_update, LLPointer<LLInventoryCallback> cb)
void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id, bool do_update, LLPointer<LLInventoryCallback> cb, const std::string description)
{
const LLInventoryItem *item = gInventory.getItem(item_id);
if (!item)
{
LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, do_update, cb);
LLDeferredCOFLinkObserver *observer = new LLDeferredCOFLinkObserver(item_id, do_update, cb, description);
gInventory.addObserver(observer);
}
else
{
addCOFItemLink(item, do_update, cb);
addCOFItemLink(item, do_update, cb, description);
}
}
@@ -2751,8 +2778,10 @@ void modified_cof_cb(const LLUUID& inv_item)
LLFloaterCustomize::getInstance()->switchToDefaultSubpart();
}
}
void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update, LLPointer<LLInventoryCallback> cb)
void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update, LLPointer<LLInventoryCallback> cb, const std::string description)
{
std::string link_description = description;
const LLViewerInventoryItem *vitem = dynamic_cast<const LLViewerInventoryItem*>(item);
if (!vitem)
{
@@ -2823,32 +2852,48 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update
{
cb = new LLBoostFuncInventoryCallback(modified_cof_cb);
}
std::string description = vitem->getIsLinkType() ? vitem->getDescription() : "";
if(description.empty())
if (vitem->getIsLinkType())
{
LLViewerWearable* wearable = gAgentWearables.getWearableFromItemID(vitem->getLinkedUUID());
if(wearable)
{
U32 index = gAgentWearables.getWearableIndex(wearable);
if(index < LLAgentWearables::MAX_CLOTHING_PER_TYPE)
{
std::ostringstream order_num;
order_num << ORDER_NUMBER_SEPARATOR << wearable->getType() * 100 + index;
description = order_num.str();
}
}
link_description = vitem->getActualDescription();
}
link_inventory_item( gAgent.getID(),
vitem->getLinkedUUID(),
getCOF(),
vitem->getName(),
description,
link_description,
LLAssetType::AT_LINK,
cb);
}
return;
}
LLInventoryModel::item_array_t LLAppearanceMgr::findCOFItemLinks(const LLUUID& item_id)
{
LLInventoryModel::item_array_t result;
const LLViewerInventoryItem *vitem =
dynamic_cast<const LLViewerInventoryItem*>(gInventory.getItem(item_id));
if (vitem)
{
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
gInventory.collectDescendents(LLAppearanceMgr::getCOF(),
cat_array,
item_array,
LLInventoryModel::EXCLUDE_TRASH);
for (S32 i=0; i<item_array.count(); i++)
{
const LLViewerInventoryItem* inv_item = item_array.get(i).get();
if (inv_item->getLinkedUUID() == vitem->getLinkedUUID())
{
result.put(item_array.get(i));
}
}
}
return result;
}
void LLAppearanceMgr::removeAllClothesFromAvatar()
{
// Fetch worn clothes (i.e. the ones in COF).
@@ -3012,7 +3057,7 @@ void LLAppearanceMgr::updateIsDirty()
if (item1->getLinkedUUID() != item2->getLinkedUUID() ||
item1->getName() != item2->getName() ||
item1->LLInventoryItem::getDescription() != item2->LLInventoryItem::getDescription())
item1->getActualDescription() != item2->getActualDescription())
{
mOutfitIsDirty = true;
return;
@@ -3223,8 +3268,8 @@ struct WearablesOrderComparator
return true;
}
const std::string& desc1 = item1->LLInventoryItem::getDescription();
const std::string& desc2 = item2->LLInventoryItem::getDescription();
const std::string& desc1 = item1->getActualDescription();
const std::string& desc2 = item2->getActualDescription();
bool item1_valid = (desc1.size() == mControlSize) && (ORDER_NUMBER_SEPARATOR == desc1[0]);
bool item2_valid = (desc2.size() == mControlSize) && (ORDER_NUMBER_SEPARATOR == desc2[0]);
@@ -3282,7 +3327,7 @@ void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id, bool update_base
if (!item) continue;
std::string new_order_str = build_order_string((LLWearableType::EType)type, i);
if (new_order_str == item->LLInventoryItem::getDescription()) continue;
if (new_order_str == item->getActualDescription()) continue;
item->setDescription(new_order_str);
item->setComplete(TRUE);
@@ -3368,13 +3413,27 @@ public:
// Successful completion.
/* virtual */ void result(const LLSD& content)
{
llinfos << "request OK" << llendl;
LL_DEBUGS("Avatar") << "content: " << ll_pretty_print_sd(content) << LL_ENDL;
if (content["success"].asBoolean())
{
LL_DEBUGS("Avatar") << "OK" << LL_ENDL;
}
else
{
onFailure(200);
}
}
// Error
/*virtual*/ void error(U32 status, const std::string& reason)
/*virtual*/ void errorWithContent(U32 status, const std::string& reason, const LLSD& content)
{
llwarns << "appearance update request failed, status: " << status << " reason: " << reason << llendl;
LL_DEBUGS("Avatar") << "content: " << ll_pretty_print_sd(content) << LL_ENDL;
onFailure(status);
}
void onFailure(U32 status)
{
F32 seconds_to_wait;
if (mRetryPolicy->shouldRetry(status,seconds_to_wait))
{
@@ -3882,8 +3941,8 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b
closer_to_body ? --it : ++it;
LLViewerInventoryItem* swap_item = *it;
if (!swap_item) return false;
std::string tmp = swap_item->LLInventoryItem::getDescription();
swap_item->setDescription(item->LLInventoryItem::getDescription());
std::string tmp = swap_item->getActualDescription();
swap_item->setDescription(item->getActualDescription());
item->setDescription(tmp);
@@ -3916,7 +3975,7 @@ void LLAppearanceMgr::sortItemsByActualDescription(LLInventoryModel::item_array_
{
if (items.size() < 2) return;
std::sort(items.begin(), items.end(), sort_by_description);
std::sort(items.begin(), items.end(), sort_by_actual_description);
}
//#define DUMP_CAT_VERBOSE
@@ -3963,7 +4022,8 @@ LLAppearanceMgr::LLAppearanceMgr():
mOutfitIsDirty(false),
mOutfitLocked(false),
mIsInUpdateAppearanceFromCOF(false),
mLastUpdateRequestCOFVersion(LLViewerInventoryCategory::VERSION_UNKNOWN)
mLastUpdateRequestCOFVersion(LLViewerInventoryCategory::VERSION_UNKNOWN),
mLastAppearanceUpdateCOFVersion(LLViewerInventoryCategory::VERSION_UNKNOWN)
{
LLOutfitObserver& outfit_observer = LLOutfitObserver::instance();

View File

@@ -108,6 +108,12 @@ public:
S32 getCOFVersion() const;
S32 mLastUpdateRequestCOFVersion;
S32 getLastUpdateRequestCOFVersion() const;
// COF version of last appearance message received for self av.
S32 mLastAppearanceUpdateCOFVersion;
S32 getLastAppearanceUpdateCOFVersion() const;
void setLastAppearanceUpdateCOFVersion(S32 new_val);
// Finds the folder link to the currently worn outfit
const LLViewerInventoryItem *getBaseOutfitLink();
@@ -142,8 +148,11 @@ public:
LLPointer<LLInventoryCallback> cb);
// Add COF link to individual item.
void addCOFItemLink(const LLUUID& item_id, bool do_update = true, LLPointer<LLInventoryCallback> cb = NULL);
void addCOFItemLink(const LLInventoryItem *item, bool do_update = true, LLPointer<LLInventoryCallback> cb = NULL);
void addCOFItemLink(const LLUUID& item_id, bool do_update = true, LLPointer<LLInventoryCallback> cb = NULL, const std::string description = "");
void addCOFItemLink(const LLInventoryItem *item, bool do_update = true, LLPointer<LLInventoryCallback> cb = NULL, const std::string description = "");
// Find COF entries referencing the given item.
LLInventoryModel::item_array_t findCOFItemLinks(const LLUUID& item_id);
// Remove COF entries
void removeCOFItemLinks(const LLUUID& item_id);

View File

@@ -193,7 +193,7 @@
#include "llimview.h"
#include "llviewerthrottle.h"
#include "llparcel.h"
#include "llviewerassetstats.h"
#include "llcommandlineparser.h"
#include "llprogressview.h"
@@ -395,6 +395,14 @@ static std::string gHelperURI;
LLAppViewer::LLUpdaterInfo *LLAppViewer::sUpdaterInfo = NULL ;
//----------------------------------------------------------------------------
// Metrics logging control constants
//----------------------------------------------------------------------------
static const F32 METRICS_INTERVAL_DEFAULT = 600.0;
static const F32 METRICS_INTERVAL_QA = 30.0;
static F32 app_metrics_interval = METRICS_INTERVAL_DEFAULT;
static bool app_metrics_qa_mode = false;
void idle_afk_check()
{
// check idle timers
@@ -670,10 +678,26 @@ bool LLAppViewer::init()
);
AIHTTPTimeoutPolicy::setDefaultCurlTimeout(policy_tmp);
}
{
// Viewer metrics initialization
//static LLCachedControl<bool> metrics_submode(gSavedSettings,
// "QAModeMetrics",
// false,
// "Enables QA features (logging, faster cycling) for metrics collector");
if (gSavedSettings.getBOOL("QAModeMetrics"))
{
app_metrics_qa_mode = true;
app_metrics_interval = METRICS_INTERVAL_QA;
}
LLViewerAssetStatsFF::init();
}
initThreads();
LL_INFOS("InitInfo") << "Threads initialized." << LL_ENDL ;
writeSystemInfo();
// Build a string representing the current version number.
@@ -742,6 +766,11 @@ bool LLAppViewer::init()
ui_audio_callback,
&LLUI::getScaleFactor()
);
// Setup notifications after LLUI::initClass() has been called.
LLNotifications::instance();
LL_INFOS("InitInfo") << "Notifications initialized." << LL_ENDL ;
LLWeb::initClass(); // do this after LLUI
LLUICtrlFactory::getInstance()->setupPaths(); // update paths with correct language set
@@ -1811,6 +1840,7 @@ bool LLAppViewer::cleanup()
gSavedSettings.cleanup();
gColors.cleanup();
gCrashSettings.cleanup();
LLViewerAssetStatsFF::cleanup();
// If we're exiting to launch an URL, do that here so the screen
// is at the right resolution before we launch IE.
@@ -1893,7 +1923,10 @@ bool LLAppViewer::initThreads()
// Image decoding
LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true);
LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true);
LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), sImageDecodeThread, enable_threads && true);
LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(),
sImageDecodeThread,
enable_threads && true,
app_metrics_qa_mode);
// Mesh streaming and caching
@@ -3119,6 +3152,15 @@ void LLAppViewer::requestQuit()
return;
}
// Try to send metrics back to the grid
metricsSend(!gDisconnected);
// Try to send last batch of avatar rez metrics.
if (!gDisconnected && isAgentAvatarValid())
{
gAgentAvatarp->updateAvatarRezMetrics(true); // force a last packet to be sent.
}
LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
effectp->setPositionGlobal(gAgent.getPositionGlobal());
effectp->setColor(LLColor4U(gAgent.getEffectColor()));
@@ -3938,6 +3980,11 @@ void LLAppViewer::idle()
llinfos << "Unknown object updates: " << gObjectList.mNumUnknownUpdates << llendl;
gObjectList.mNumUnknownUpdates = 0;
}
// ViewerMetrics FPS piggy-backing on the debug timer.
// The 5-second interval is nice for this purpose. If the object debug
// bit moves or is disabled, please give this a suitable home.
LLViewerAssetStatsFF::record_fps_main(gFPSClamped);
}
gFrameStats.addFrameData();
}
@@ -3985,6 +4032,19 @@ void LLAppViewer::idle()
gInventory.idleNotifyObservers();
}
// Metrics logging (LLViewerAssetStats, etc.)
{
static LLTimer report_interval;
// *TODO: Add configuration controls for this
F32 seconds = report_interval.getElapsedTimeF32();
if (seconds >= app_metrics_interval)
{
metricsSend(! gDisconnected);
report_interval.reset();
}
}
if (gDisconnected)
{
return;
@@ -4737,3 +4797,71 @@ void LLAppViewer::handleLoginComplete()
writeDebugInfo();
}
/**
* LLViewerAssetStats collects data on a per-region (as defined by the agent's
* location) so we need to tell it about region changes which become a kind of
* hidden variable/global state in the collectors. For collectors not running
* on the main thread, we need to send a message to move the data over safely
* and cheaply (amortized over a run).
*/
void LLAppViewer::metricsUpdateRegion(U64 region_handle)
{
if (0 != region_handle)
{
LLViewerAssetStatsFF::set_region_main(region_handle);
if (LLAppViewer::sTextureFetch)
{
// Send a region update message into 'thread1' to get the new region.
LLAppViewer::sTextureFetch->commandSetRegion(region_handle);
}
else
{
// No 'thread1', a.k.a. TextureFetch, so update directly
LLViewerAssetStatsFF::set_region_thread1(region_handle);
}
}
}
/**
* Attempts to start a multi-threaded metrics report to be sent back to
* the grid for consumption.
*/
void LLAppViewer::metricsSend(bool enable_reporting)
{
if (! gViewerAssetStatsMain)
return;
if (LLAppViewer::sTextureFetch)
{
LLViewerRegion * regionp = gAgent.getRegion();
if (enable_reporting && regionp)
{
std::string caps_url = regionp->getCapability("ViewerMetrics");
// Make a copy of the main stats to send into another thread.
// Receiving thread takes ownership.
LLViewerAssetStats * main_stats(new LLViewerAssetStats(*gViewerAssetStatsMain));
// Send a report request into 'thread1' to get the rest of the data
// and provide some additional parameters while here.
LLAppViewer::sTextureFetch->commandSendMetrics(caps_url,
gAgentSessionID,
gAgentID,
main_stats);
main_stats = 0; // Ownership transferred
}
else
{
LLAppViewer::sTextureFetch->commandDataBreak();
}
}
// Reset even if we can't report. Rather than gather up a huge chunk of
// data, we'll keep to our sampling interval and retain the data
// resolution in time.
gViewerAssetStatsMain->reset();
}

View File

@@ -168,6 +168,10 @@ public:
void addOnIdleCallback(const boost::function<void()>& cb); // add a callback to fire (once) when idle
void purgeCache(); // Clear the local cache.
// Metrics policy helper statics.
static void metricsUpdateRegion(U64 region_handle);
static void metricsSend(bool enable_reporting);
protected:
virtual bool initWindow(); // Initialize the viewer's window.
virtual bool initLogging(); // Initialize log files, logging system, return false on failure.

View File

@@ -250,11 +250,17 @@ S32 LLDrawable::findReferences(LLDrawable *drawablep)
return count;
}
static LLFastTimer::DeclareTimer FTM_ALLOCATE_FACE("Allocate Face", true);
LLFace* LLDrawable::addFace(LLFacePool *poolp, LLViewerTexture *texturep)
{
LLMemType mt(LLMemType::MTYPE_DRAWABLE);
LLFace *face = new LLFace(this, mVObjp);
LLFace *face;
{
LLFastTimer t(FTM_ALLOCATE_FACE);
face = new LLFace(this, mVObjp);
}
if (!face) llerrs << "Allocating new Face: " << mFaces.size() << llendl;
if (face)
@@ -276,10 +282,12 @@ LLFace* LLDrawable::addFace(LLFacePool *poolp, LLViewerTexture *texturep)
LLFace* LLDrawable::addFace(const LLTextureEntry *te, LLViewerTexture *texturep)
{
LLMemType mt(LLMemType::MTYPE_DRAWABLE);
LLFace *face;
face = new LLFace(this, mVObjp);
{
LLFastTimer t(FTM_ALLOCATE_FACE);
face = new LLFace(this, mVObjp);
}
face->setTEOffset(mFaces.size());
face->setTexture(texturep);
@@ -537,6 +545,12 @@ F32 LLDrawable::updateXform(BOOL undamped)
}
}
}
else
{
dist_squared = dist_vec_squared(old_pos, target_pos);
dist_squared += (1.f - dot(old_rot, target_rot)) * 10.f;
dist_squared += dist_vec_squared(old_scale, target_scale);
}
LLVector3 vec = mCurrentScale-target_scale;

View File

@@ -1278,6 +1278,9 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace*
face->setGeomIndex(0);
face->setIndicesIndex(0);
//rigged faces do not batch textures
face->setTextureIndex(255);
if (buffer.isNull() || buffer->getTypeMask() != data_mask || !buffer->isWriteable())
{ //make a new buffer
if (sShaderLevel > 0)
@@ -1636,6 +1639,7 @@ void LLDrawPoolAvatar::renderRiggedAlpha(LLVOAvatar* avatar)
LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
renderRigged(avatar, RIGGED_ALPHA);
gGL.setColorMask(true, false);
}
}
@@ -1652,6 +1656,7 @@ void LLDrawPoolAvatar::renderRiggedFullbrightAlpha(LLVOAvatar* avatar)
LLRender::BF_ONE_MINUS_SOURCE_ALPHA);
renderRigged(avatar, RIGGED_FULLBRIGHT_ALPHA);
gGL.setColorMask(true, false);
}
}

View File

@@ -1192,7 +1192,7 @@ static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_MIN_MAX("Min/Max");
static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_RGB2LUM("RGB to Luminance");
static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_RESCALE("Rescale");
static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_GEN_NORMAL("Generate Normal");
static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_CREATE("Create");
static LLFastTimer::DeclareTimer FTM_BUMP_SOURCE_CREATE("Bump Source Create");
// static
void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerTexture *src_vi, LLImageRaw* src, LLUUID& source_asset_id, EBumpEffect bump_code )

View File

@@ -48,7 +48,7 @@ std::vector<LLVolumeImplFlexible*> LLVolumeImplFlexible::sInstanceList;
std::vector<U32> LLVolumeImplFlexible::sUpdateDelay;
static LLFastTimer::DeclareTimer FTM_FLEXIBLE_REBUILD("Rebuild");
static LLFastTimer::DeclareTimer FTM_DO_FLEXIBLE_UPDATE("Update");
static LLFastTimer::DeclareTimer FTM_DO_FLEXIBLE_UPDATE("Flexible Update");
// LLFlexibleObjectData::pack/unpack now in llprimitive.cpp
@@ -361,7 +361,7 @@ void LLVolumeImplFlexible::doIdleUpdate()
if (visible)
{
if (!drawablep->isState(LLDrawable::IN_REBUILD_Q1) &&
mVO->getPixelArea() > 256.f)
pixel_area > 256.f)
{
U32 id;

View File

@@ -180,9 +180,10 @@ void LLFloaterColorPicker::createUI ()
//
void LLFloaterColorPicker::showUI ()
{
open(); /*Flawfinder: ignore*/
setVisible ( TRUE );
setFocus ( TRUE );
open(); /*Flawfinder: ignore*/
// HACK: if system color picker is required - close the SL one we made and use default system dialog
if ( gSavedSettings.getBOOL ( "UseDefaultColorPicker" ) )

View File

@@ -78,6 +78,7 @@
#include "llpaneleditwearable.h"
#include "llmakeoutfitdialog.h"
#include "llagentcamera.h"
#include "llappearancemgr.h"
#include "statemachine/aifilepicker.h"
@@ -256,6 +257,7 @@ void LLFloaterCustomize::delayedClose(bool proceed, bool app_quitting)
{
LLVOAvatarSelf::onCustomizeEnd();
LLFloater::onClose(app_quitting);
if(gAgentAvatarp)gAgentAvatarp->mSpecialRenderMode = 0;
}
}
@@ -425,17 +427,7 @@ void LLFloaterCustomize::onBtnExport_continued(AIFilePicker* filepicker)
void LLFloaterCustomize::onBtnOk()
{
gAgentWearables.saveAllWearables();
if ( gAgentAvatarp )
{
gAgentAvatarp->invalidateAll();
gAgentAvatarp->requestLayerSetUploads();
gAgent.sendAgentSetAppearance();
}
saveCurrentWearables();
gFloaterView->sendChildToBack(this);
close(false);
}
@@ -595,7 +587,10 @@ void LLFloaterCustomize::updateVisiblity(bool force_disable_camera_switch/*=fals
if(!getVisible())
{
if(force_disable_camera_switch || !gAgentCamera.cameraCustomizeAvatar() || !gAgentCamera.getCameraAnimating() || (gMorphView && gMorphView->getVisible()))
{
if(gAgentAvatarp)gAgentAvatarp->mSpecialRenderMode = 3;
setVisibleAndFrontmost(TRUE);
}
}
}
@@ -621,12 +616,8 @@ void LLFloaterCustomize::askToSaveIfDirty( boost::function<void (BOOL)> cb )
}
}
bool LLFloaterCustomize::onSaveDialog(const LLSD& notification, const LLSD& response )
void LLFloaterCustomize::saveCurrentWearables()
{
S32 option = LLNotification::getSelectedOption(notification, response);
BOOL proceed = FALSE;
LLWearableType::EType cur = getCurrentWearableType();
for(U32 i = 0;i < gAgentWearables.getWearableCount(cur);++i)
@@ -634,32 +625,71 @@ bool LLFloaterCustomize::onSaveDialog(const LLSD& notification, const LLSD& resp
LLViewerWearable* wearable = gAgentWearables.getViewerWearable(cur,i);
if(wearable && wearable->isDirty())
{
switch( option )
/*
===============================================================================================
Copy-pasted some code from LLPanelEditWearable::saveChanges instead of just calling saveChanges, as
we only have one 'active' panel per wearable type, not per layer. The panels just update when
layer of focus is changed. Easier just to do it right here manually.
===============================================================================================
*/
// Find an existing link to this wearable's inventory item, if any, and its description field.
if(gAgentAvatarp->isUsingServerBakes())
{
case 0: // "Save"
gAgentWearables.saveWearable( cur, i );
proceed = TRUE;
break;
case 1: // "Don't Save"
LLInventoryItem *link_item = NULL;
std::string description;
LLInventoryModel::item_array_t links =
LLAppearanceMgr::instance().findCOFItemLinks(wearable->getItemID());
if (links.size()>0)
{
gAgentWearables.revertWearable( cur, i );
proceed = TRUE;
link_item = links.get(0).get();
if (link_item && link_item->getIsLinkType())
{
description = link_item->getActualDescription();
}
}
break;
// Make another copy of this link, with the same
// description. This is needed to bump the COF
// version so texture baking service knows appearance has changed.
if (link_item)
{
// Create new link
link_inventory_item( gAgent.getID(),
link_item->getLinkedUUID(),
LLAppearanceMgr::instance().getCOF(),
link_item->getName(),
description,
LLAssetType::AT_LINK,
NULL);
// Remove old link
gInventory.purgeObject(link_item->getUUID());
}
}
gAgentWearables.saveWearable( cur, i );
}
}
}
case 2: // "Cancel"
break;
default:
llassert(0);
break;
bool LLFloaterCustomize::onSaveDialog(const LLSD& notification, const LLSD& response )
{
S32 option = LLNotification::getSelectedOption(notification, response);
if(option == 0)
{
saveCurrentWearables();
}
else
{
LLWearableType::EType cur = getCurrentWearableType();
for(U32 i = 0;i < gAgentWearables.getWearableCount(cur);++i)
{
LLViewerWearable* wearable = gAgentWearables.getViewerWearable(cur,i);
if(wearable && wearable->isDirty())
{
gAgentWearables.revertWearable( cur, i );
}
}
}
mNextStepAfterSaveCallback(proceed);
mNextStepAfterSaveCallback(option < 2);
mNextStepAfterSaveCallback.disconnect_all_slots(); //Should this be done?
return false;

View File

@@ -117,6 +117,7 @@ private:
public:
void askToSaveIfDirty( boost::function<void (BOOL)> cb );
void saveCurrentWearables();
void switchToDefaultSubpart();
private:

View File

@@ -741,7 +741,7 @@ void LLPanelLandGeneral::refresh()
mBtnReleaseLand->setEnabled( can_release );
}
BOOL use_pass = parcel->getParcelFlag(PF_USE_PASS_LIST) && !LLViewerParcelMgr::getInstance()->isCollisionBanned();;
BOOL use_pass = parcel->getOwnerID()!= gAgent.getID() && parcel->getParcelFlag(PF_USE_PASS_LIST) && !LLViewerParcelMgr::getInstance()->isCollisionBanned();;
mBtnBuyPass->setEnabled(use_pass);
}
}

View File

@@ -4781,6 +4781,10 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
}
else
{
if ((flags & FIRST_SELECTED_ITEM) == 0)
{
disabled_items.push_back(std::string("Open"));
}
addOpenRightClickMenuOption(items);
items.push_back(std::string("Properties"));
@@ -5961,7 +5965,8 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
}
// [/RLVa:KB]*/
if ((flags & FIRST_SELECTED_ITEM) == 0)
bool not_modifiable = !gAgentWearables.isWearableModifiable(item->getUUID());
if (((flags & FIRST_SELECTED_ITEM) == 0) || not_modifiable)
{
disabled_items.push_back(std::string("Wearable Edit"));
}

View File

@@ -72,7 +72,6 @@ void LLManip::rebuild(LLViewerObject* vobj)
LLDrawable* drawablep = vobj->mDrawable;
if (drawablep && drawablep->getVOVolume())
{
gPipeline.markRebuild(drawablep,LLDrawable::REBUILD_VOLUME, TRUE);
drawablep->setState(LLDrawable::MOVE_UNDAMPED); // force to UNDAMPED
drawablep->updateMove();
@@ -82,6 +81,14 @@ void LLManip::rebuild(LLViewerObject* vobj)
group->dirtyGeom();
gPipeline.markRebuild(group, TRUE);
}
LLViewerObject::const_child_list_t& child_list = vobj->getChildren();
for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(), endIter = child_list.end();
iter != endIter; ++iter)
{
LLViewerObject* child = *iter;
rebuild(child);
}
}
}

View File

@@ -92,7 +92,7 @@ void LLMorphView::initialize()
}
gAgentAvatarp->stopMotion( ANIM_AGENT_BODY_NOISE );
gAgentAvatarp->mSpecialRenderMode = 3;
//gAgentAvatarp->mSpecialRenderMode = 3;
// set up camera for close look at avatar
mOldCameraNearClip = LLViewerCamera::getInstance()->getNear();
@@ -107,7 +107,7 @@ void LLMorphView::shutdown()
if (isAgentAvatarValid())
{
gAgentAvatarp->startMotion( ANIM_AGENT_BODY_NOISE );
gAgentAvatarp->mSpecialRenderMode = 0;
//gAgentAvatarp->mSpecialRenderMode = 0;
// reset camera
LLViewerCamera::getInstance()->setNear(mOldCameraNearClip);
}

View File

@@ -1241,11 +1241,30 @@ void LLPanelEditWearable::saveChanges(bool force_save_as, std::string new_name)
U32 index = gAgentWearables.getWearableIndex(getWearable());
// Find an existing link to this wearable's inventory item, if any, and its description field.
LLInventoryItem *link_item = NULL;
std::string description;
if(gAgentAvatarp->isUsingServerBakes())
{
LLInventoryModel::item_array_t links =
LLAppearanceMgr::instance().findCOFItemLinks(getWearable()->getItemID());
if (links.size()>0)
{
link_item = links.get(0).get();
if (link_item && link_item->getIsLinkType())
{
description = link_item->getActualDescription();
}
}
}
if (force_save_as)
{
// the name of the wearable has changed, re-save wearable with new name
LLAppearanceMgr::instance().removeCOFItemLinks(getWearable()->getItemID());
LLViewerWearable* new_wearable = gAgentWearables.saveWearableAs(mType, index, new_name, FALSE);
LLViewerWearable* new_wearable = gAgentWearables.saveWearableAs(mType, index, new_name, description, FALSE);
if(new_wearable)
{
mPendingWearable = new_wearable;
@@ -1256,7 +1275,23 @@ void LLPanelEditWearable::saveChanges(bool force_save_as, std::string new_name)
}
else
{
gAgentWearables.saveWearable(mType, index, TRUE, new_name);
// Make another copy of this link, with the same
// description. This is needed to bump the COF
// version so texture baking service knows appearance has changed.
if (link_item)
{
// Create new link
link_inventory_item( gAgent.getID(),
link_item->getLinkedUUID(),
LLAppearanceMgr::instance().getCOF(),
link_item->getName(),
description,
LLAssetType::AT_LINK,
NULL);
// Remove old link
gInventory.purgeObject(link_item->getUUID());
}
gAgentWearables.saveWearable(mType, index, TRUE, new_name);
}
}

View File

@@ -430,7 +430,7 @@ void LLPanelPermissions::refresh()
}
}
childSetEnabled("button set group",owners_identical && (mOwnerID == gAgent.getID()) && is_nonpermanent_enforced);
childSetEnabled("button set group",root_selected && owners_identical && (mOwnerID == gAgent.getID()) && is_nonpermanent_enforced);
childSetEnabled("button open group", group_id.notNull());
// figure out the contents of the name, description, & category

View File

@@ -0,0 +1,152 @@
/**
* @file llsimplestat.h
* @brief Runtime statistics accumulation.
*
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_SIMPLESTAT_H
#define LL_SIMPLESTAT_H
// History
//
// The original source for this code is the server repositories'
// llcommon/llstat.h file. This particular code was added after the
// viewer/server code schism but before the effort to convert common
// code to libraries was complete. Rather than add to merge issues,
// the needed code was cut'n'pasted into this new header as it isn't
// too awful a burden. Post-modularization, we can look at removing
// this redundancy.
/**
* @class LLSimpleStatCounter
* @brief Just counts events.
*
* Really not needed but have a pattern in mind in the future.
* Interface limits what can be done at that's just fine.
*
* *TODO: Update/transfer unit tests
* Unit tests: indra/test/llcommon_llstat_tut.cpp
*/
class LLSimpleStatCounter
{
public:
inline LLSimpleStatCounter() { reset(); }
// Default destructor and assignment operator are valid
inline void reset() { mCount = 0; }
inline void merge(const LLSimpleStatCounter & src)
{ mCount += src.mCount; }
inline U32 operator++() { return ++mCount; }
inline U32 getCount() const { return mCount; }
protected:
U32 mCount;
};
/**
* @class LLSimpleStatMMM
* @brief Templated collector of min, max and mean data for stats.
*
* Fed a stream of data samples, keeps a running account of the
* min, max and mean seen since construction or the last reset()
* call. A freshly-constructed or reset instance returns counts
* and values of zero.
*
* Overflows and underflows (integer, inf or -inf) and NaN's
* are the caller's problem. As is loss of precision when
* the running sum's exponent (when parameterized by a floating
* point of some type) differs from a given data sample's.
*
* Unit tests: indra/test/llcommon_llstat_tut.cpp
*/
template <typename VALUE_T = F32>
class LLSimpleStatMMM
{
public:
typedef VALUE_T Value;
public:
LLSimpleStatMMM() { reset(); }
// Default destructor and assignment operator are valid
/**
* Resets the object returning all counts and derived
* values back to zero.
*/
void reset()
{
mCount = 0;
mMin = Value(0);
mMax = Value(0);
mTotal = Value(0);
}
void record(Value v)
{
if (mCount)
{
mMin = llmin(mMin, v);
mMax = llmax(mMax, v);
}
else
{
mMin = v;
mMax = v;
}
mTotal += v;
++mCount;
}
void merge(const LLSimpleStatMMM<VALUE_T> & src)
{
if (! mCount)
{
*this = src;
}
else if (src.mCount)
{
mMin = llmin(mMin, src.mMin);
mMax = llmax(mMax, src.mMax);
mCount += src.mCount;
mTotal += src.mTotal;
}
}
inline U32 getCount() const { return mCount; }
inline Value getMin() const { return mMin; }
inline Value getMax() const { return mMax; }
inline Value getMean() const { return mCount ? mTotal / mCount : mTotal; }
protected:
U32 mCount;
Value mMin;
Value mMax;
Value mTotal;
};
#endif // LL_SIMPLESTAT_H

View File

@@ -224,6 +224,7 @@
// </edit>
#include "llpathfindingmanager.h"
#include "llevents.h"
#include "llavatarnamecache.h"
#include "lgghunspell_wrapper.h"
@@ -281,6 +282,11 @@ static LLHost gFirstSim;
static std::string gFirstSimSeedCap;
static LLVector3 gAgentStartLookAt(1.0f, 0.f, 0.f);
static std::string gAgentStartLocation = "safe";
boost::scoped_ptr<LLEventPump> LLStartUp::sStateWatcher(new LLEventStream("StartupState"));
boost::scoped_ptr<LLViewerStats::PhaseMap> LLStartUp::sPhases(new LLViewerStats::PhaseMap);
//
// local function declaration
//
@@ -423,6 +429,15 @@ bool idle_startup()
if ( STATE_FIRST == LLStartUp::getStartupState() )
{
static bool first_call = true;
if (first_call)
{
// Other phases get handled when startup state changes,
// need to capture the initial state as well.
LLStartUp::getPhases().startPhase(LLStartUp::getStartupStateString());
first_call = false;
}
gViewerWindow->showCursor();
gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT);
@@ -1885,7 +1900,7 @@ bool idle_startup()
display_startup();
//reset statistics
LLViewerStats::getInstance()->resetStats();
LLViewerStats::instance().resetStats();
if (!gNoRender)
{
@@ -3649,11 +3664,23 @@ std::string LLStartUp::startupStateToString(EStartupState state)
void LLStartUp::setStartupState( EStartupState state )
{
LL_INFOS("AppInit") << "Startup state changing from " <<
startupStateToString(gStartupState) << " to " <<
getStartupStateString() << " to " <<
startupStateToString(state) << LL_ENDL;
getPhases().stopPhase(getStartupStateString());
gStartupState = state;
getPhases().startPhase(getStartupStateString());
postStartupState();
}
void LLStartUp::postStartupState()
{
LLSD stateInfo;
stateInfo["str"] = getStartupStateString();
stateInfo["enum"] = gStartupState;
sStateWatcher->post(stateInfo);
}
void reset_login()
{

View File

@@ -33,7 +33,12 @@
#ifndef LL_LLSTARTUP_H
#define LL_LLSTARTUP_H
class LLViewerTexture;
#include <boost/scoped_ptr.hpp>
class LLViewerTexture ;
class LLEventPump;
#include "llviewerstats.h"
// functions
bool idle_startup();
@@ -101,7 +106,6 @@ public:
static void initNameCache();
static void cleanupNameCache();
// outfit_folder_name can be a folder anywhere in your inventory,
@@ -128,15 +132,20 @@ public:
// if we have a SLURL or sim string ("Ahern/123/45") that started
// the viewer, dispatch it
static void postStartupState();
static std::string sSLURLCommand;
// *HACK: On startup, if we were passed a secondlife://app/do/foo
// command URL, store it for later processing.
static bool startLLProxy(); // Initialize the SOCKS 5 proxy
static LLViewerStats::PhaseMap& getPhases() { return *sPhases; }
private:
static std::string startupStateToString(EStartupState state);
static EStartupState gStartupState; // Do not set directly, use LLStartup::setStartupState
static boost::scoped_ptr<LLEventPump> sStateWatcher;
static boost::scoped_ptr<LLViewerStats::PhaseMap> sPhases;
};

View File

@@ -54,13 +54,17 @@
#include "llviewerregion.h"
#include "llviewerstats.h"
#include "llviewerstatsrecorder.h"
#include "llviewerassetstats.h"
#include "llworld.h"
#include "llsdutil.h"
#include "llstartup.h"
#include "llsdserialize.h"
#include "llbuffer.h"
class AIHTTPTimeoutPolicy;
extern AIHTTPTimeoutPolicy HTTPGetResponder_timeout;
extern AIHTTPTimeoutPolicy lcl_responder_timeout;
extern AIHTTPTimeoutPolicy assetReportHandler_timeout;
LLStat LLTextureFetch::sCacheHitRate("texture_cache_hits", 128);
LLStat LLTextureFetch::sCacheReadLatency("texture_cache_read_latency", 128);
@@ -200,6 +204,12 @@ private:
bool processSimulatorPackets();
bool writeToCacheComplete();
// Threads: Ttf
void recordTextureStart(bool is_http);
// Threads: Ttf
void recordTextureDone(bool is_http);
void lockWorkMutex() { mWorkMutex.lock(); }
void unlockWorkMutex() { mWorkMutex.unlock(); }
@@ -295,6 +305,11 @@ private:
S32 mLastPacket;
U16 mTotalPackets;
U8 mImageCodec;
LLViewerAssetStats::duration_t mMetricsStartTime;
// State history
U32 mCacheReadCount;
U32 mCacheWriteCount;
};
//////////////////////////////////////////////////////////////////////////////
@@ -387,6 +402,7 @@ public:
}
mFetcher->removeFromHTTPQueue(mID, data_size);
worker->recordTextureDone(true);
}
else
{
@@ -503,6 +519,229 @@ static bool sgConnectionThrottle() {
}
#endif
//////////////////////////////////////////////////////////////////////////////
// Cross-thread messaging for asset metrics.
/**
* @brief Base class for cross-thread requests made of the fetcher
*
* I believe the intent of the LLQueuedThread class was to
* have these operations derived from LLQueuedThread::QueuedRequest
* but the texture fetcher has elected to manage the queue
* in its own manner. So these are free-standing objects which are
* managed in simple FIFO order on the mCommands queue of the
* LLTextureFetch object.
*
* What each represents is a simple command sent from an
* outside thread into the TextureFetch thread to be processed
* in order and in a timely fashion (though not an absolute
* higher priority than other operations of the thread).
* Each operation derives a new class from the base customizing
* members, constructors and the doWork() method to effect
* the command.
*
* The flow is one-directional. There are two global instances
* of the LLViewerAssetStats collector, one for the main program's
* thread pointed to by gViewerAssetStatsMain and one for the
* TextureFetch thread pointed to by gViewerAssetStatsThread1.
* Common operations has each thread recording metrics events
* into the respective collector unconcerned with locking and
* the state of any other thread. But when the agent moves into
* a different region or the metrics timer expires and a report
* needs to be sent back to the grid, messaging across threads
* is required to distribute data and perform global actions.
* In pseudo-UML, it looks like:
*
* Main Thread1
* . .
* . .
* +-----+ .
* | AM | .
* +--+--+ .
* +-------+ | .
* | Main | +--+--+ .
* | | | SRE |---. .
* | Stats | +-----+ \ .
* | | | \ (uuid) +-----+
* | Coll. | +--+--+ `-------->| SR |
* +-------+ | MSC | +--+--+
* | ^ +-----+ |
* | | (uuid) / . +-----+ (uuid)
* | `--------' . | MSC |---------.
* | . +-----+ |
* | +-----+ . v
* | | TE | . +-------+
* | +--+--+ . | Thd1 |
* | | . | |
* | +-----+ . | Stats |
* `--------->| RSC | . | |
* +--+--+ . | Coll. |
* | . +-------+
* +--+--+ . |
* | SME |---. . |
* +-----+ \ . |
* . \ (clone) +-----+ |
* . `-------->| SM | |
* . +--+--+ |
* . | |
* . +-----+ |
* . | RSC |<--------'
* . +-----+
* . |
* . +-----+
* . | CP |--> HTTP POST
* . +-----+
* . .
* . .
*
*
* Key:
*
* SRE - Set Region Enqueued. Enqueue a 'Set Region' command in
* the other thread providing the new UUID of the region.
* TFReqSetRegion carries the data.
* SR - Set Region. New region UUID is sent to the thread-local
* collector.
* SME - Send Metrics Enqueued. Enqueue a 'Send Metrics' command
* including an ownership transfer of a cloned LLViewerAssetStats.
* TFReqSendMetrics carries the data.
* SM - Send Metrics. Global metrics reporting operation. Takes
* the cloned stats from the command, merges it with the
* thread's local stats, converts to LLSD and sends it on
* to the grid.
* AM - Agent Moved. Agent has completed some sort of move to a
* new region.
* TE - Timer Expired. Metrics timer has expired (on the order
* of 10 minutes).
* CP - CURL Post
* MSC - Modify Stats Collector. State change in the thread-local
* collector. Typically a region change which affects the
* global pointers used to find the 'current stats'.
* RSC - Read Stats Collector. Extract collector data cloning it
* (i.e. deep copy) when necessary.
*
*/
class LLTextureFetch::TFRequest // : public LLQueuedThread::QueuedRequest
{
public:
// Default ctors and assignment operator are correct.
virtual ~TFRequest()
{}
// Patterned after QueuedRequest's method but expected behavior
// is different. Always expected to complete on the first call
// and work dispatcher will assume the same and delete the
// request after invocation.
virtual bool doWork(LLTextureFetch * fetcher) = 0;
};
namespace
{
/**
* @brief Implements a 'Set Region' cross-thread command.
*
* When an agent moves to a new region, subsequent metrics need
* to be binned into a new or existing stats collection in 1:1
* relationship with the region. We communicate this region
* change across the threads involved in the communication with
* this message.
*
* Corresponds to LLTextureFetch::commandSetRegion()
*/
class TFReqSetRegion : public LLTextureFetch::TFRequest
{
public:
TFReqSetRegion(U64 region_handle)
: LLTextureFetch::TFRequest(),
mRegionHandle(region_handle)
{}
TFReqSetRegion & operator=(const TFReqSetRegion &); // Not defined
virtual ~TFReqSetRegion()
{}
virtual bool doWork(LLTextureFetch * fetcher);
public:
const U64 mRegionHandle;
};
/**
* @brief Implements a 'Send Metrics' cross-thread command.
*
* This is the big operation. The main thread gathers metrics
* for a period of minutes into LLViewerAssetStats and other
* objects then makes a snapshot of the data by cloning the
* collector. This command transfers the clone, along with a few
* additional arguments (UUIDs), handing ownership to the
* TextureFetch thread. It then merges its own data into the
* cloned copy, converts to LLSD and kicks off an HTTP POST of
* the resulting data to the currently active metrics collector.
*
* Corresponds to LLTextureFetch::commandSendMetrics()
*/
class TFReqSendMetrics : public LLTextureFetch::TFRequest
{
public:
/**
* Construct the 'Send Metrics' command to have the TextureFetch
* thread add and log metrics data.
*
* @param caps_url URL of a "ViewerMetrics" Caps target
* to receive the data. Does not have to
* be associated with a particular region.
*
* @param session_id UUID of the agent's session.
*
* @param agent_id UUID of the agent. (Being pure here...)
*
* @param main_stats Pointer to a clone of the main thread's
* LLViewerAssetStats data. Thread1 takes
* ownership of the copy and disposes of it
* when done.
*/
TFReqSendMetrics(const std::string & caps_url,
const LLUUID & session_id,
const LLUUID & agent_id,
LLViewerAssetStats * main_stats)
: LLTextureFetch::TFRequest(),
mCapsURL(caps_url),
mSessionID(session_id),
mAgentID(agent_id),
mMainStats(main_stats)
{}
TFReqSendMetrics & operator=(const TFReqSendMetrics &); // Not defined
virtual ~TFReqSendMetrics();
virtual bool doWork(LLTextureFetch * fetcher);
public:
const std::string mCapsURL;
const LLUUID mSessionID;
const LLUUID mAgentID;
LLViewerAssetStats * mMainStats;
};
/*
* Examines the merged viewer metrics report and if found to be too long,
* will attempt to truncate it in some reasonable fashion.
*
* @param max_regions Limit of regions allowed in report.
*
* @param metrics Full, merged viewer metrics report.
*
* @returns If data was truncated, returns true.
*/
bool truncate_viewer_metrics(int max_regions, LLSD & metrics);
} // end of anonymous namespace
//////////////////////////////////////////////////////////////////////////////
//static
@@ -522,6 +761,9 @@ const char* LLTextureFetchWorker::sStateDescs[] = {
"DONE",
};
// static
volatile bool LLTextureFetch::svMetricsDataBreak(true); // Start with a data break
// called from MAIN THREAD
LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
@@ -570,7 +812,10 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
mFirstPacket(0),
mLastPacket(-1),
mTotalPackets(0),
mImageCodec(IMG_CODEC_INVALID)
mImageCodec(IMG_CODEC_INVALID),
mMetricsStartTime(0),
mCacheReadCount(0U),
mCacheWriteCount(0U)
{
mCanUseNET = mUrl.empty() ;
@@ -605,6 +850,7 @@ LLTextureFetchWorker::~LLTextureFetchWorker()
clearPackets();
unlockWorkMutex();
mFetcher->removeFromHTTPQueue(mID);
mFetcher->updateStateStats(mCacheReadCount, mCacheWriteCount);
}
void LLTextureFetchWorker::clearPackets()
@@ -818,6 +1064,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
// read file from local disk
++mCacheReadCount;
std::string filename = mUrl.substr(7, std::string::npos);
CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority,
@@ -828,6 +1075,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
{
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
++mCacheReadCount;
CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
offset, size, responder);
@@ -963,6 +1211,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
mRequestedDiscard = mDesiredDiscard;
mSentRequest = QUEUED;
mFetcher->addToNetworkQueue(this);
recordTextureStart(false);
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
return false;
@@ -997,11 +1246,13 @@ bool LLTextureFetchWorker::doWork(S32 param)
setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
mState = DECODE_IMAGE;
mWriteToCacheState = SHOULD_WRITE;
recordTextureDone(false);
}
else
{
mFetcher->addToNetworkQueue(this); // failsafe
setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
recordTextureStart(false);
}
return false;
}
@@ -1404,6 +1655,7 @@ bool LLTextureFetchWorker::doWork(S32 param)
U32 cache_priority = mWorkPriority;
mWritten = FALSE;
mState = WAIT_ON_WRITE;
++mCacheWriteCount;
CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID);
mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority,
mFormattedImage->getData(), datasize,
@@ -1762,6 +2014,35 @@ bool LLTextureFetchWorker::writeToCacheComplete()
}
// Threads: Ttf
void LLTextureFetchWorker::recordTextureStart(bool is_http)
{
if (! mMetricsStartTime)
{
mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
}
LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
is_http,
LLImageBase::TYPE_AVATAR_BAKE == mType);
}
// Threads: Ttf
void LLTextureFetchWorker::recordTextureDone(bool is_http)
{
if (mMetricsStartTime)
{
LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
is_http,
LLImageBase::TYPE_AVATAR_BAKE == mType,
LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime);
mMetricsStartTime = 0;
}
LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
is_http,
LLImageBase::TYPE_AVATAR_BAKE == mType);
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// public
@@ -1776,7 +2057,10 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image
mImageDecodeThread(imagedecodethread),
mTextureBandwidth(0),
mHTTPTextureBits(0),
mTotalHTTPRequests(0)
mTotalHTTPRequests(0),
mQAMode(qa_mode),
mTotalCacheReadCount(0U),
mTotalCacheWriteCount(0U)
{
mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
}
@@ -2108,6 +2392,7 @@ S32 LLTextureFetch::getPending()
return res;
}
// Locks: Ct
// virtual
bool LLTextureFetch::runCondition()
{
@@ -2120,7 +2405,15 @@ bool LLTextureFetch::runCondition()
//
// Changes here may need to be reflected in getPending().
return ! (mRequestQueue.empty() && mIdleThread); // From base class
bool have_no_commands(false);
{
LLMutexLock lock(&mQueueMutex); // +Mfq
have_no_commands = mCommands.empty();
} // -Mfq
return ! (have_no_commands
&& (mRequestQueue.empty() && mIdleThread)); // From base class
}
//////////////////////////////////////////////////////////////////////////////
@@ -2128,6 +2421,8 @@ bool LLTextureFetch::runCondition()
// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs)
void LLTextureFetch::commonUpdate()
{
// Run a cross-thread command, if any.
cmdDoWork();
}
@@ -2185,7 +2480,21 @@ void LLTextureFetch::shutDownImageDecodeThread()
}
}
// WORKER THREAD
// Threads: Ttf
void LLTextureFetch::startThread()
{
}
// Threads: Ttf
void LLTextureFetch::endThread()
{
LL_INFOS("Texture") << "CacheReads: " << mTotalCacheReadCount
<< ", CacheWrites: " << mTotalCacheWriteCount
<< ", TotalHTTPReq: " << getTotalNumHTTPRequests()
<< LL_ENDL;
}
// Threads: Ttf
void LLTextureFetch::threadedUpdate()
{
// Limit update frequency
@@ -2480,10 +2789,10 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8
}
if (!res)
{
mNetworkQueueMutex.lock(); // +Mfnq
++mBadPacketCount;
mNetworkQueueMutex.lock() ;
mCancelQueue[host].insert(id);
mNetworkQueueMutex.unlock() ;
mNetworkQueueMutex.unlock(); // -Mfnq
return false;
}
@@ -2530,10 +2839,10 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1
}
if (!res)
{
mNetworkQueueMutex.lock(); // +Mfnq
++mBadPacketCount;
mNetworkQueueMutex.lock() ;
mCancelQueue[host].insert(id);
mNetworkQueueMutex.unlock() ;
mNetworkQueueMutex.unlock(); // -Mfnq
return false;
}
@@ -2664,3 +2973,264 @@ void LLTextureFetch::dump()
llinfos << " ID: " << (*iter) << llendl;
}
}
// Threads: T*
void LLTextureFetch::updateStateStats(U32 cache_read, U32 cache_write)
{
LLMutexLock lock(&mQueueMutex); // +Mfq
mTotalCacheReadCount += cache_read;
mTotalCacheWriteCount += cache_write;
} // -Mfq
// Threads: T*
void LLTextureFetch::getStateStats(U32 * cache_read, U32 * cache_write)
{
U32 ret1(0U), ret2(0U);
{
LLMutexLock lock(&mQueueMutex); // +Mfq
ret1 = mTotalCacheReadCount;
ret2 = mTotalCacheWriteCount;
} // -Mfq
*cache_read = ret1;
*cache_write = ret2;
}
// Threads: T*
void LLTextureFetch::commandSetRegion(U64 region_handle)
{
TFReqSetRegion * req = new TFReqSetRegion(region_handle);
cmdEnqueue(req);
}
// Threads: T*
void LLTextureFetch::commandSendMetrics(const std::string & caps_url,
const LLUUID & session_id,
const LLUUID & agent_id,
LLViewerAssetStats * main_stats)
{
TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats);
cmdEnqueue(req);
}
// Threads: T*
void LLTextureFetch::commandDataBreak()
{
// The pedantically correct way to implement this is to create a command
// request object in the above fashion and enqueue it. However, this is
// simple data of an advisorial not operational nature and this case
// of shared-write access is tolerable.
LLTextureFetch::svMetricsDataBreak = true;
}
// Threads: T*
void LLTextureFetch::cmdEnqueue(TFRequest * req)
{
lockQueue(); // +Mfq
mCommands.push_back(req);
unlockQueue(); // -Mfq
unpause();
}
// Threads: T*
LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue()
{
TFRequest * ret = 0;
lockQueue(); // +Mfq
if (! mCommands.empty())
{
ret = mCommands.front();
mCommands.erase(mCommands.begin());
}
unlockQueue(); // -Mfq
return ret;
}
// Threads: Ttf
void LLTextureFetch::cmdDoWork()
{
if (mDebugPause)
{
return; // debug: don't do any work
}
TFRequest * req = cmdDequeue();
if (req)
{
// One request per pass should really be enough for this.
req->doWork(this);
delete req;
}
}
//////////////////////////////////////////////////////////////////////////////
// Private (anonymous) class methods implementing the command scheme.
namespace
{
// Example of a simple notification handler for metrics
// delivery notification. Earlier versions of the code used
// a Responder that tried harder to detect delivery breaks
// but it really isn't that important. If someone wants to
// revisit that effort, here is a place to start.
class AssetReportHandler : public LLHTTPClient::ResponderWithCompleted
{
public:
// Threads: Ttf
/*virtual*/ virtual void completed(U32 status, std::string const& reason, LLSD const& content)
{
if (status)
{
LL_WARNS("Texture") << "Successfully delivered asset metrics to grid."
<< LL_ENDL;
}
else
{
LL_WARNS("Texture") << "Error delivering asset metrics to grid. Reason: "
<< status << LL_ENDL;
}
}
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return assetReportHandler_timeout; }
/*virtual*/ char const* getName(void) const { return "AssetReportHandler"; }
}; // end class AssetReportHandler
/**
* Implements the 'Set Region' command.
*
* Thread: Thread1 (TextureFetch)
*/
bool
TFReqSetRegion::doWork(LLTextureFetch *)
{
LLViewerAssetStatsFF::set_region_thread1(mRegionHandle);
return true;
}
TFReqSendMetrics::~TFReqSendMetrics()
{
delete mMainStats;
mMainStats = 0;
}
/**
* Implements the 'Send Metrics' command. Takes over
* ownership of the passed LLViewerAssetStats pointer.
*
* Thread: Thread1 (TextureFetch)
*/
bool
TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
{
static const U32 report_priority(1);
if (! gViewerAssetStatsThread1)
return true;
static volatile bool reporting_started(false);
static volatile S32 report_sequence(0);
// We've taken over ownership of the stats copy at this
// point. Get a working reference to it for merging here
// but leave it in 'this'. Destructor will rid us of it.
LLViewerAssetStats & main_stats = *mMainStats;
// Merge existing stats into those from main, convert to LLSD
main_stats.merge(*gViewerAssetStatsThread1);
LLSD merged_llsd = main_stats.asLLSD(true);
// Add some additional meta fields to the content
merged_llsd["session_id"] = mSessionID;
merged_llsd["agent_id"] = mAgentID;
merged_llsd["message"] = "ViewerAssetMetrics"; // Identifies the type of metrics
merged_llsd["sequence"] = report_sequence; // Sequence number
merged_llsd["initial"] = ! reporting_started; // Initial data from viewer
merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak; // Break in data prior to this report
// Update sequence number
if (S32_MAX == ++report_sequence)
report_sequence = 0;
reporting_started = true;
// Limit the size of the stats report if necessary.
merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd);
if (! mCapsURL.empty())
{
if(fetcher->isQAMode() || true) //Assuming the 'true' will vanish eventually.
LLHTTPClient::post(mCapsURL, merged_llsd, new AssetReportHandler());
else
LLHTTPClient::post(mCapsURL, merged_llsd, new LLHTTPClient::ResponderIgnore());
LLTextureFetch::svMetricsDataBreak = false;
}
else
{
LLTextureFetch::svMetricsDataBreak = true;
}
// In QA mode, Metrics submode, log the result for ease of testing
if (fetcher->isQAMode())
{
LL_INFOS("Textures") << ll_pretty_print_sd(merged_llsd) << LL_ENDL;
}
gViewerAssetStatsThread1->reset();
return true;
}
bool
truncate_viewer_metrics(int max_regions, LLSD & metrics)
{
static const LLSD::String reg_tag("regions");
static const LLSD::String duration_tag("duration");
LLSD & reg_map(metrics[reg_tag]);
if (reg_map.size() <= max_regions)
{
return false;
}
// Build map of region hashes ordered by duration
typedef std::multimap<LLSD::Real, int> reg_ordered_list_t;
reg_ordered_list_t regions_by_duration;
int ind(0);
LLSD::array_const_iterator it_end(reg_map.endArray());
for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind)
{
LLSD::Real duration = (*it)[duration_tag].asReal();
regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind));
}
// Build a replacement regions array with the longest-persistence regions
LLSD new_region(LLSD::emptyArray());
reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend());
reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin());
for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2)
{
new_region.append(reg_map[it2->second]);
}
reg_map = new_region;
return true;
}
} // end of anonymous namespace

View File

@@ -33,19 +33,24 @@
#ifndef LL_LLTEXTUREFETCH_H
#define LL_LLTEXTUREFETCH_H
#include <vector>
#include <map>
#include "lldir.h"
#include "llimage.h"
#include "lluuid.h"
#include "llworkerthread.h"
#include "lltextureinfo.h"
#include "llapr.h"
#include "llstat.h"
class LLViewerTexture;
class LLTextureFetchWorker;
class HTTPGetResponder;
class LLTextureCache;
class LLImageDecodeThread;
class LLHost;
class LLViewerAssetStats;
class LLTextureCache;
// Interface class
class LLTextureFetch : public LLWorkerThread
@@ -93,8 +98,23 @@ public:
LLTextureFetchWorker* getWorker(const LLUUID& id);
LLTextureFetchWorker* getWorkerAfterLock(const LLUUID& id);
LLTextureInfo* getTextureInfo() { return &mTextureInfo; }
// Commands available to other threads to control metrics gathering operations.
// Threads: T*
void commandSetRegion(U64 region_handle);
// Threads: T*
void commandSendMetrics(const std::string & caps_url,
const LLUUID & session_id,
const LLUUID & agent_id,
LLViewerAssetStats * main_stats);
// Threads: T*
void commandDataBreak();
bool isQAMode() const { return mQAMode; }
void updateStateStats(U32 cache_read, U32 cache_write);
void getStateStats(U32 * cache_read, U32 * cache_write);
protected:
void addToNetworkQueue(LLTextureFetchWorker* worker);
void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel);
@@ -107,9 +127,14 @@ protected:
private:
void sendRequestListToSimulators();
/*virtual*/ void startThread(void);
/*virtual*/ void endThread(void);
/*virtual*/ void threadedUpdate(void);
void commonUpdate();
void cmdEnqueue(TFRequest *);
TFRequest * cmdDequeue();
void cmdDoWork();
public:
LLUUID mDebugID;
@@ -148,6 +173,25 @@ private:
//debug use
U32 mTotalHTTPRequests ;
// Out-of-band cross-thread command queue. This command queue
// is logically tied to LLQueuedThread's list of
// QueuedRequest instances and so must be covered by the
// same locks.
typedef std::vector<TFRequest *> command_queue_t;
command_queue_t mCommands;
// If true, modifies some behaviors that help with QA tasks.
const bool mQAMode;
// Cumulative stats on the states/requests issued by
// textures running through here.
U32 mTotalCacheReadCount;
U32 mTotalCacheWriteCount;
public:
// A probabilistically-correct indicator that the current
// attempt to log metrics follows a break in the metrics stream
// reporting due to either startup or a problem POSTing data.
static volatile bool svMetricsDataBreak;
};
#endif // LL_LLTEXTUREFETCH_H

View File

@@ -57,6 +57,7 @@
#include "llviewerwindow.h"
#include "llvoavatarself.h"
#include "llmorphview.h"
#include "llfloatercustomize.h"
// Globals
BOOL gCameraBtnZoom = TRUE;
@@ -232,6 +233,7 @@ void LLToolCamera::pickCallback(const LLPickInfo& pick_info)
if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgentCamera.getCameraMode() )
{
if(CAMERA_MODE_CUSTOMIZE_AVATAR == gAgentCamera.getCameraMode())
gAgentCamera.setFocusOnAvatar(FALSE, FALSE);
LLVector3d cam_pos = gAgentCamera.getCameraPositionGlobal();

View File

@@ -0,0 +1,611 @@
/**
* @file llviewerassetstats.cpp
* @brief
*
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llviewerassetstats.h"
#include "llregionhandle.h"
#include "stdtypes.h"
/*
* Classes and utility functions for per-thread and per-region
* asset and experiential metrics to be aggregated grid-wide.
*
* The basic metrics grouping is LLViewerAssetStats::PerRegionStats.
* This provides various counters and simple statistics for asset
* fetches binned into a few categories. One of these is maintained
* for each region encountered and the 'current' region is available
* as a simple reference. Each thread (presently two) interested
* in participating in these stats gets an instance of the
* LLViewerAssetStats class so that threads are completely
* independent.
*
* The idea of a current region is used for simplicity and speed
* of categorization. Each metrics event could have taken a
* region uuid argument resulting in a suitable lookup. Arguments
* against this design include:
*
* - Region uuid not trivially available to caller.
* - Cost (cpu, disruption in real work flow) too high.
* - Additional precision not really meaningful.
*
* By itself, the LLViewerAssetStats class is thread- and
* viewer-agnostic and can be used anywhere without assumptions
* of global pointers and other context. For the viewer,
* a set of free functions are provided in the namespace
* LLViewerAssetStatsFF which *do* implement viewer-native
* policies about per-thread globals and will do correct
* defensive tests of same.
*
* References
*
* Project:
* <TBD>
*
* Test Plan:
* <TBD>
*
* Jiras:
* <TBD>
*
* Unit Tests:
* indra/newview/tests/llviewerassetstats_test.cpp
*
*/
// ------------------------------------------------------
// Global data definitions
// ------------------------------------------------------
LLViewerAssetStats * gViewerAssetStatsMain(0);
LLViewerAssetStats * gViewerAssetStatsThread1(0);
// ------------------------------------------------------
// Local declarations
// ------------------------------------------------------
namespace
{
static LLViewerAssetStats::EViewerAssetCategories
asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp);
}
// ------------------------------------------------------
// LLViewerAssetStats::PerRegionStats struct definition
// ------------------------------------------------------
void
LLViewerAssetStats::PerRegionStats::reset()
{
for (int i(0); i < LL_ARRAY_SIZE(mRequests); ++i)
{
mRequests[i].mEnqueued.reset();
mRequests[i].mDequeued.reset();
mRequests[i].mResponse.reset();
}
mFPS.reset();
mTotalTime = 0;
mStartTimestamp = LLViewerAssetStatsFF::get_timestamp();
}
void
LLViewerAssetStats::PerRegionStats::merge(const LLViewerAssetStats::PerRegionStats & src)
{
// mRegionHandle, mTotalTime, mStartTimestamp are left alone.
// mFPS
if (src.mFPS.getCount() && mFPS.getCount())
{
mFPS.merge(src.mFPS);
}
// Requests
for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i)
{
mRequests[i].mEnqueued.merge(src.mRequests[i].mEnqueued);
mRequests[i].mDequeued.merge(src.mRequests[i].mDequeued);
mRequests[i].mResponse.merge(src.mRequests[i].mResponse);
}
}
void
LLViewerAssetStats::PerRegionStats::accumulateTime(duration_t now)
{
mTotalTime += (now - mStartTimestamp);
mStartTimestamp = now;
}
// ------------------------------------------------------
// LLViewerAssetStats class definition
// ------------------------------------------------------
LLViewerAssetStats::LLViewerAssetStats()
: mRegionHandle(U64(0))
{
reset();
}
LLViewerAssetStats::LLViewerAssetStats(const LLViewerAssetStats & src)
: mRegionHandle(src.mRegionHandle),
mResetTimestamp(src.mResetTimestamp)
{
const PerRegionContainer::const_iterator it_end(src.mRegionStats.end());
for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it)
{
mRegionStats[it->first] = new PerRegionStats(*it->second);
}
mCurRegionStats = mRegionStats[mRegionHandle];
}
void
LLViewerAssetStats::reset()
{
// Empty the map of all region stats
mRegionStats.clear();
// If we have a current stats, reset it, otherwise, as at construction,
// create a new one as we must always have a current stats block.
if (mCurRegionStats)
{
mCurRegionStats->reset();
}
else
{
mCurRegionStats = new PerRegionStats(mRegionHandle);
}
// And add reference to map
mRegionStats[mRegionHandle] = mCurRegionStats;
// Start timestamp consistent with per-region collector
mResetTimestamp = mCurRegionStats->mStartTimestamp;
}
void
LLViewerAssetStats::setRegion(region_handle_t region_handle)
{
if (region_handle == mRegionHandle)
{
// Already active, ignore.
return;
}
// Get duration for current set
const duration_t now = LLViewerAssetStatsFF::get_timestamp();
mCurRegionStats->accumulateTime(now);
// Prepare new set
PerRegionContainer::iterator new_stats = mRegionStats.find(region_handle);
if (mRegionStats.end() == new_stats)
{
// Haven't seen this region_id before, create a new block and make it current.
mCurRegionStats = new PerRegionStats(region_handle);
mRegionStats[region_handle] = mCurRegionStats;
}
else
{
mCurRegionStats = new_stats->second;
}
mCurRegionStats->mStartTimestamp = now;
mRegionHandle = region_handle;
}
void
LLViewerAssetStats::recordGetEnqueued(LLViewerAssetType::EType at, bool with_http, bool is_temp)
{
const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
++(mCurRegionStats->mRequests[int(eac)].mEnqueued);
}
void
LLViewerAssetStats::recordGetDequeued(LLViewerAssetType::EType at, bool with_http, bool is_temp)
{
const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
++(mCurRegionStats->mRequests[int(eac)].mDequeued);
}
void
LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, duration_t duration)
{
const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
mCurRegionStats->mRequests[int(eac)].mResponse.record(duration);
}
void
LLViewerAssetStats::recordFPS(F32 fps)
{
mCurRegionStats->mFPS.record(fps);
}
LLSD
LLViewerAssetStats::asLLSD(bool compact_output)
{
// Top-level tags
static const LLSD::String tags[EVACCount] =
{
LLSD::String("get_texture_temp_http"),
LLSD::String("get_texture_temp_udp"),
LLSD::String("get_texture_non_temp_http"),
LLSD::String("get_texture_non_temp_udp"),
LLSD::String("get_wearable_udp"),
LLSD::String("get_sound_udp"),
LLSD::String("get_gesture_udp"),
LLSD::String("get_other")
};
// Stats Group Sub-tags.
static const LLSD::String enq_tag("enqueued");
static const LLSD::String deq_tag("dequeued");
static const LLSD::String rcnt_tag("resp_count");
static const LLSD::String rmin_tag("resp_min");
static const LLSD::String rmax_tag("resp_max");
static const LLSD::String rmean_tag("resp_mean");
// MMM Group Sub-tags.
static const LLSD::String cnt_tag("count");
static const LLSD::String min_tag("min");
static const LLSD::String max_tag("max");
static const LLSD::String mean_tag("mean");
const duration_t now = LLViewerAssetStatsFF::get_timestamp();
mCurRegionStats->accumulateTime(now);
LLSD regions = LLSD::emptyArray();
for (PerRegionContainer::iterator it = mRegionStats.begin();
mRegionStats.end() != it;
++it)
{
if (0 == it->first)
{
// Never emit NULL UUID/handle in results.
continue;
}
PerRegionStats & stats = *it->second;
LLSD reg_stat = LLSD::emptyMap();
for (int i = 0; i < LL_ARRAY_SIZE(tags); ++i)
{
PerRegionStats::prs_group & group(stats.mRequests[i]);
if ((! compact_output) ||
group.mEnqueued.getCount() ||
group.mDequeued.getCount() ||
group.mResponse.getCount())
{
LLSD & slot = reg_stat[tags[i]];
slot = LLSD::emptyMap();
slot[enq_tag] = LLSD(S32(stats.mRequests[i].mEnqueued.getCount()));
slot[deq_tag] = LLSD(S32(stats.mRequests[i].mDequeued.getCount()));
slot[rcnt_tag] = LLSD(S32(stats.mRequests[i].mResponse.getCount()));
slot[rmin_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMin() * 1.0e-6));
slot[rmax_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMax() * 1.0e-6));
slot[rmean_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMean() * 1.0e-6));
}
}
if ((! compact_output) || stats.mFPS.getCount())
{
LLSD & slot = reg_stat["fps"];
slot = LLSD::emptyMap();
slot[cnt_tag] = LLSD(S32(stats.mFPS.getCount()));
slot[min_tag] = LLSD(F64(stats.mFPS.getMin()));
slot[max_tag] = LLSD(F64(stats.mFPS.getMax()));
slot[mean_tag] = LLSD(F64(stats.mFPS.getMean()));
}
U32 grid_x(0), grid_y(0);
grid_from_region_handle(it->first, &grid_x, &grid_y);
reg_stat["grid_x"] = LLSD::Integer(grid_x);
reg_stat["grid_y"] = LLSD::Integer(grid_y);
reg_stat["duration"] = LLSD::Real(stats.mTotalTime * 1.0e-6);
regions.append(reg_stat);
}
LLSD ret = LLSD::emptyMap();
ret["regions"] = regions;
ret["duration"] = LLSD::Real((now - mResetTimestamp) * 1.0e-6);
return ret;
}
void
LLViewerAssetStats::merge(const LLViewerAssetStats & src)
{
// mRegionHandle, mCurRegionStats and mResetTimestamp are left untouched.
// Just merge the stats bodies
const PerRegionContainer::const_iterator it_end(src.mRegionStats.end());
for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it)
{
PerRegionContainer::iterator dst(mRegionStats.find(it->first));
if (mRegionStats.end() == dst)
{
// Destination is missing data, just make a private copy
mRegionStats[it->first] = new PerRegionStats(*it->second);
}
else
{
dst->second->merge(*it->second);
}
}
}
// ------------------------------------------------------
// Global free-function definitions (LLViewerAssetStatsFF namespace)
// ------------------------------------------------------
namespace LLViewerAssetStatsFF
{
//
// Target thread is elaborated in the function name. This could
// have been something 'templatey' like specializations iterated
// over a set of constants but with so few, this is clearer I think.
//
// As for the threads themselves... rather than do fine-grained
// locking as we gather statistics, this code creates a collector
// for each thread, allocated and run independently. Logging
// happens at relatively infrequent intervals and at that time
// the data is sent to a single thread to be aggregated into
// a single entity with locks, thread safety and other niceties.
//
// A particularly fussy implementation would distribute the
// per-thread pointers across separate cache lines. But that should
// be beyond current requirements.
//
// 'main' thread - initial program thread
void
set_region_main(LLViewerAssetStats::region_handle_t region_handle)
{
if (! gViewerAssetStatsMain)
return;
gViewerAssetStatsMain->setRegion(region_handle);
}
void
record_enqueue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp)
{
if (! gViewerAssetStatsMain)
return;
gViewerAssetStatsMain->recordGetEnqueued(at, with_http, is_temp);
}
void
record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp)
{
if (! gViewerAssetStatsMain)
return;
gViewerAssetStatsMain->recordGetDequeued(at, with_http, is_temp);
}
void
record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration)
{
if (! gViewerAssetStatsMain)
return;
gViewerAssetStatsMain->recordGetServiced(at, with_http, is_temp, duration);
}
void
record_fps_main(F32 fps)
{
if (! gViewerAssetStatsMain)
return;
gViewerAssetStatsMain->recordFPS(fps);
}
// 'thread1' - should be for TextureFetch thread
void
set_region_thread1(LLViewerAssetStats::region_handle_t region_handle)
{
if (! gViewerAssetStatsThread1)
return;
gViewerAssetStatsThread1->setRegion(region_handle);
}
void
record_enqueue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp)
{
if (! gViewerAssetStatsThread1)
return;
gViewerAssetStatsThread1->recordGetEnqueued(at, with_http, is_temp);
}
void
record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp)
{
if (! gViewerAssetStatsThread1)
return;
gViewerAssetStatsThread1->recordGetDequeued(at, with_http, is_temp);
}
void
record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration)
{
if (! gViewerAssetStatsThread1)
return;
gViewerAssetStatsThread1->recordGetServiced(at, with_http, is_temp, duration);
}
void
init()
{
if (! gViewerAssetStatsMain)
{
gViewerAssetStatsMain = new LLViewerAssetStats();
}
if (! gViewerAssetStatsThread1)
{
gViewerAssetStatsThread1 = new LLViewerAssetStats();
}
}
void
cleanup()
{
delete gViewerAssetStatsMain;
gViewerAssetStatsMain = 0;
delete gViewerAssetStatsThread1;
gViewerAssetStatsThread1 = 0;
}
} // namespace LLViewerAssetStatsFF
// ------------------------------------------------------
// Local function definitions
// ------------------------------------------------------
namespace
{
LLViewerAssetStats::EViewerAssetCategories
asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp)
{
// For statistical purposes, we divide GETs into several
// populations of asset fetches:
// - textures which are de-prioritized in the asset system
// - wearables (clothing, bodyparts) which directly affect
// user experiences when they log in
// - sounds
// - gestures
// - everything else.
//
llassert_always(50 == LLViewerAssetType::AT_COUNT);
// Multiple asset definitions are floating around so this requires some
// maintenance and attention.
static const LLViewerAssetStats::EViewerAssetCategories asset_to_bin_map[LLViewerAssetType::AT_COUNT] =
{
LLViewerAssetStats::EVACTextureTempHTTPGet, // (0) AT_TEXTURE
LLViewerAssetStats::EVACSoundUDPGet, // AT_SOUND
LLViewerAssetStats::EVACOtherGet, // AT_CALLINGCARD
LLViewerAssetStats::EVACOtherGet, // AT_LANDMARK
LLViewerAssetStats::EVACOtherGet, // AT_SCRIPT
LLViewerAssetStats::EVACWearableUDPGet, // AT_CLOTHING
LLViewerAssetStats::EVACOtherGet, // AT_OBJECT
LLViewerAssetStats::EVACOtherGet, // AT_NOTECARD
LLViewerAssetStats::EVACOtherGet, // AT_CATEGORY
LLViewerAssetStats::EVACOtherGet, // AT_ROOT_CATEGORY
LLViewerAssetStats::EVACOtherGet, // (10) AT_LSL_TEXT
LLViewerAssetStats::EVACOtherGet, // AT_LSL_BYTECODE
LLViewerAssetStats::EVACOtherGet, // AT_TEXTURE_TGA
LLViewerAssetStats::EVACWearableUDPGet, // AT_BODYPART
LLViewerAssetStats::EVACOtherGet, // AT_TRASH
LLViewerAssetStats::EVACOtherGet, // AT_SNAPSHOT_CATEGORY
LLViewerAssetStats::EVACOtherGet, // AT_LOST_AND_FOUND
LLViewerAssetStats::EVACSoundUDPGet, // AT_SOUND_WAV
LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_TGA
LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_JPEG
LLViewerAssetStats::EVACGestureUDPGet, // (20) AT_ANIMATION
LLViewerAssetStats::EVACGestureUDPGet, // AT_GESTURE
LLViewerAssetStats::EVACOtherGet, // AT_SIMSTATE
LLViewerAssetStats::EVACOtherGet, // AT_FAVORITE
LLViewerAssetStats::EVACOtherGet, // AT_LINK
LLViewerAssetStats::EVACOtherGet, // AT_LINK_FOLDER
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, // (30)
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, // (40)
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, //
LLViewerAssetStats::EVACOtherGet, // AT_MESH
// (50)
};
if (at < 0 || at >= LLViewerAssetType::AT_COUNT)
{
return LLViewerAssetStats::EVACOtherGet;
}
LLViewerAssetStats::EViewerAssetCategories ret(asset_to_bin_map[at]);
if (LLViewerAssetStats::EVACTextureTempHTTPGet == ret)
{
// Indexed with [is_temp][with_http]
static const LLViewerAssetStats::EViewerAssetCategories texture_bin_map[2][2] =
{
{
LLViewerAssetStats::EVACTextureNonTempUDPGet,
LLViewerAssetStats::EVACTextureNonTempHTTPGet,
},
{
LLViewerAssetStats::EVACTextureTempUDPGet,
LLViewerAssetStats::EVACTextureTempHTTPGet,
}
};
ret = texture_bin_map[is_temp][with_http];
}
return ret;
}
} // anonymous namespace

View File

@@ -0,0 +1,327 @@
/**
* @file llviewerassetstats.h
* @brief Client-side collection of asset request statistics
*
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLVIEWERASSETSTATUS_H
#define LL_LLVIEWERASSETSTATUS_H
#include "linden_common.h"
#include "llpointer.h"
#include "llrefcount.h"
#include "llviewerassettype.h"
#include "llviewerassetstorage.h"
#include "llsimplestat.h"
#include "llsd.h"
/**
* @class LLViewerAssetStats
* @brief Records performance aspects of asset access operations.
*
* This facility is derived from a very similar simulator-based
* one, LLSimAssetStats. It's function is to count asset access
* operations and characterize response times. Collected data
* are binned in several dimensions:
*
* - Asset types collapsed into a few aggregated categories
* - By simulator UUID
* - By transport mechanism (HTTP vs MessageSystem)
* - By persistence (temp vs non-temp)
*
* Statistics collected are fairly basic at this point:
*
* - Counts of enqueue and dequeue operations
* - Min/Max/Mean of asset transfer operations
*
* This collector differs from the simulator-based on in a
* number of ways:
*
* - The front-end/back-end distinction doesn't exist in viewer
* code
* - Multiple threads must be safely accomodated in the viewer
*
* Access to results is by conversion to an LLSD with some standardized
* key names. The intent of this structure is that it be emitted as
* standard syslog-based metrics formatting where it can be picked
* up by interested parties.
*
* For convenience, a set of free functions in namespace
* LLViewerAssetStatsFF is provided for conditional test-and-call
* operations.
*/
class LLViewerAssetStats
{
public:
enum EViewerAssetCategories
{
EVACTextureTempHTTPGet, //< Texture GETs - temp/baked, HTTP
EVACTextureTempUDPGet, //< Texture GETs - temp/baked, UDP
EVACTextureNonTempHTTPGet, //< Texture GETs - perm, HTTP
EVACTextureNonTempUDPGet, //< Texture GETs - perm, UDP
EVACWearableUDPGet, //< Wearable GETs
EVACSoundUDPGet, //< Sound GETs
EVACGestureUDPGet, //< Gesture GETs
EVACOtherGet, //< Other GETs
EVACCount // Must be last
};
/**
* Type for duration and other time values in the metrics. Selected
* for compatibility with the pre-existing timestamp on the texture
* fetcher class, LLTextureFetch.
*/
typedef U64 duration_t;
/**
* Type for the region identifier used in stats. Currently uses
* the region handle's type (a U64) rather than the regions's LLUUID
* as the latter isn't available immediately.
*/
typedef U64 region_handle_t;
/**
* @brief Collected data for a single region visited by the avatar.
*
* Fairly simple, for each asset bin enumerated above a count
* of enqueue and dequeue operations and simple stats on response
* times for completed requests.
*/
class PerRegionStats : public LLRefCount
{
public:
PerRegionStats(const region_handle_t region_handle)
: LLRefCount(),
mRegionHandle(region_handle)
{
reset();
}
PerRegionStats(const PerRegionStats & src)
: LLRefCount(),
mRegionHandle(src.mRegionHandle),
mTotalTime(src.mTotalTime),
mStartTimestamp(src.mStartTimestamp),
mFPS(src.mFPS)
{
for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i)
{
mRequests[i] = src.mRequests[i];
}
}
// Default assignment and destructor are correct.
void reset();
void merge(const PerRegionStats & src);
// Apply current running time to total and reset start point.
// Return current timestamp as a convenience.
void accumulateTime(duration_t now);
public:
region_handle_t mRegionHandle;
duration_t mTotalTime;
duration_t mStartTimestamp;
LLSimpleStatMMM<> mFPS;
struct prs_group
{
LLSimpleStatCounter mEnqueued;
LLSimpleStatCounter mDequeued;
LLSimpleStatMMM<duration_t> mResponse;
}
mRequests [EVACCount];
};
public:
LLViewerAssetStats();
LLViewerAssetStats(const LLViewerAssetStats &);
// Default destructor is correct.
LLViewerAssetStats & operator=(const LLViewerAssetStats &); // Not defined
// Clear all metrics data. This leaves the currently-active region
// in place but with zero'd data for all metrics. All other regions
// are removed from the collection map.
void reset();
// Set hidden region argument and establish context for subsequent
// collection calls.
void setRegion(region_handle_t region_handle);
// Asset GET Requests
void recordGetEnqueued(LLViewerAssetType::EType at, bool with_http, bool is_temp);
void recordGetDequeued(LLViewerAssetType::EType at, bool with_http, bool is_temp);
void recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, duration_t duration);
// Frames-Per-Second Samples
void recordFPS(F32 fps);
// Merge a source instance into a destination instance. This is
// conceptually an 'operator+=()' method:
// - counts are added
// - minimums are min'd
// - maximums are max'd
// - other scalars are ignored ('this' wins)
//
void merge(const LLViewerAssetStats & src);
// Retrieve current metrics for all visited regions (NULL region UUID/handle excluded)
// Returned LLSD is structured as follows:
//
// &stats_group = {
// enqueued : int,
// dequeued : int,
// resp_count : int,
// resp_min : float,
// resp_max : float,
// resp_mean : float
// }
//
// &mmm_group = {
// count : int,
// min : float,
// max : float,
// mean : float
// }
//
// {
// duration: int
// regions: {
// $: { // Keys are strings of the region's handle in hex
// duration: : int,
// fps: : &mmm_group,
// get_texture_temp_http : &stats_group,
// get_texture_temp_udp : &stats_group,
// get_texture_non_temp_http : &stats_group,
// get_texture_non_temp_udp : &stats_group,
// get_wearable_udp : &stats_group,
// get_sound_udp : &stats_group,
// get_gesture_udp : &stats_group,
// get_other : &stats_group
// }
// }
// }
//
// @param compact_output If true, omits from conversion any mmm_block
// or stats_block that would contain all zero data.
// Useful for transmission when the receiver knows
// what is expected and will assume zero for missing
// blocks.
LLSD asLLSD(bool compact_output);
protected:
typedef std::map<region_handle_t, LLPointer<PerRegionStats> > PerRegionContainer;
// Region of the currently-active region. Always valid but may
// be zero after construction or when explicitly set. Unchanged
// by a reset() call.
region_handle_t mRegionHandle;
// Pointer to metrics collection for currently-active region. Always
// valid and unchanged after reset() though contents will be changed.
// Always points to a collection contained in mRegionStats.
LLPointer<PerRegionStats> mCurRegionStats;
// Metrics data for all regions during one collection cycle
PerRegionContainer mRegionStats;
// Time of last reset
duration_t mResetTimestamp;
};
/**
* Global stats collectors one for each independent thread where
* assets and other statistics are gathered. The globals are
* expected to be created at startup time and then picked up by
* their respective threads afterwards. A set of free functions
* are provided to access methods behind the globals while both
* minimally disrupting visual flow and supplying a description
* of intent.
*
* Expected thread assignments:
*
* - Main: main() program execution thread
* - Thread1: TextureFetch worker thread
*/
extern LLViewerAssetStats * gViewerAssetStatsMain;
extern LLViewerAssetStats * gViewerAssetStatsThread1;
namespace LLViewerAssetStatsFF
{
/**
* @brief Allocation and deallocation of globals.
*
* init() should be called before threads are started that will access it though
* you'll likely get away with calling it afterwards. cleanup() should only be
* called after threads are shutdown to prevent races on the global pointers.
*/
void init();
void cleanup();
/**
* We have many timers, clocks etc. in the runtime. This is the
* canonical timestamp for these metrics which is compatible with
* the pre-existing timestamping in the texture fetcher.
*/
inline LLViewerAssetStats::duration_t get_timestamp()
{
return LLTimer::getTotalTime();
}
/**
* Region context, event and duration loggers for the Main thread.
*/
void set_region_main(LLViewerAssetStats::region_handle_t region_handle);
void record_enqueue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp);
void record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp);
void record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp,
LLViewerAssetStats::duration_t duration);
void record_fps_main(F32 fps);
/**
* Region context, event and duration loggers for Thread 1.
*/
void set_region_thread1(LLViewerAssetStats::region_handle_t region_handle);
void record_enqueue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp);
void record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp);
void record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp,
LLViewerAssetStats::duration_t duration);
} // namespace LLViewerAssetStatsFF
#endif // LL_LLVIEWERASSETSTATUS_H

View File

@@ -2,44 +2,92 @@
* @file llviewerassetstorage.cpp
* @brief Subclass capable of loading asset data to/from an external source.
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
* Copyright (C) 2010, Linden Research, Inc.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "linden_common.h"
#include "llagent.h"
#include "llviewerassetstorage.h"
#include "llviewerbuild.h"
#include "llvfile.h"
#include "llvfs.h"
#include "message.h"
#include "llagent.h"
#include "lltransfersourceasset.h"
#include "lltransfertargetvfile.h"
#include "llviewerassetstats.h"
///----------------------------------------------------------------------------
/// LLViewerAssetRequest
///----------------------------------------------------------------------------
/**
* @brief Local class to encapsulate asset fetch requests with a timestamp.
*
* Derived from the common LLAssetRequest class, this is currently used
* only for fetch/get operations and its only function is to wrap remote
* asset fetch requests so that they can be timed.
*/
class LLViewerAssetRequest : public LLAssetRequest
{
public:
LLViewerAssetRequest(const LLUUID &uuid, const LLAssetType::EType type)
: LLAssetRequest(uuid, type),
mMetricsStartTime(0)
{
}
LLViewerAssetRequest & operator=(const LLViewerAssetRequest &); // Not defined
// Default assignment operator valid
// virtual
~LLViewerAssetRequest()
{
recordMetrics();
}
protected:
void recordMetrics()
{
if (mMetricsStartTime)
{
// Okay, it appears this request was used for useful things. Record
// the expected dequeue and duration of request processing.
LLViewerAssetStatsFF::record_dequeue_main(mType, false, false);
LLViewerAssetStatsFF::record_response_main(mType, false, false,
(LLViewerAssetStatsFF::get_timestamp()
- mMetricsStartTime));
mMetricsStartTime = 0;
}
}
public:
LLViewerAssetStats::duration_t mMetricsStartTime;
};
///----------------------------------------------------------------------------
/// LLViewerAssetStorage
///----------------------------------------------------------------------------
LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
LLVFS *vfs, LLVFS *static_vfs,
@@ -68,7 +116,7 @@ void LLViewerAssetStorage::storeAssetData(
F64 timeout)
{
LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
llinfos << "LLViewerAssetStorage::storeAssetData (legacy) " << tid << ":" << LLAssetType::lookup(asset_type)
LL_DEBUGS("AssetStorage") << "LLViewerAssetStorage::storeAssetData (legacy) " << tid << ":" << LLAssetType::lookup(asset_type)
<< " ASSET_ID: " << asset_id << llendl;
if (mUpstreamHost.isOk())
@@ -201,9 +249,9 @@ void LLViewerAssetStorage::storeAssetData(
}
LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
llinfos << "LLViewerAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
LL_DEBUGS("AssetStorage") << "LLViewerAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
llinfos << "ASSET_ID: " << asset_id << llendl;
LL_DEBUGS("AssetStorage") << "ASSET_ID: " << asset_id << llendl;
S32 size = 0;
LLFILE* fp = LLFile::fopen(filename, "rb");
@@ -266,3 +314,77 @@ void LLViewerAssetStorage::storeAssetData(
}
}
}
/**
* @brief Allocate and queue an asset fetch request for the viewer
*
* This is a nearly-verbatim copy of the base class's implementation
* with the following changes:
* - Use a locally-derived request class
* - Start timing for metrics when request is queued
*
* This is an unfortunate implementation choice but it's forced by
* current conditions. A refactoring that might clean up the layers
* of responsibility or introduce factories or more virtualization
* of methods would enable a more attractive solution.
*
* If LLAssetStorage::_queueDataRequest changes, this must change
* as well.
*/
// virtual
void LLViewerAssetStorage::_queueDataRequest(
const LLUUID& uuid,
LLAssetType::EType atype,
LLGetAssetCallback callback,
void *user_data,
BOOL duplicate,
BOOL is_priority)
{
if (mUpstreamHost.isOk())
{
// stash the callback info so we can find it after we get the response message
LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype);
req->mDownCallback = callback;
req->mUserData = user_data;
req->mIsPriority = is_priority;
if (!duplicate)
{
// Only collect metrics for non-duplicate requests. Others
// are piggy-backing and will artificially lower averages.
req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
}
mPendingDownloads.push_back(req);
if (!duplicate)
{
// send request message to our upstream data provider
// Create a new asset transfer.
LLTransferSourceParamsAsset spa;
spa.setAsset(uuid, atype);
// Set our destination file, and the completion callback.
LLTransferTargetParamsVFile tpvf;
tpvf.setAsset(uuid, atype);
tpvf.setCallback(downloadCompleteCallback, req);
LL_DEBUGS("AssetStorage") << "Starting transfer for " << uuid << llendl;
LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET);
ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f));
LLViewerAssetStatsFF::record_enqueue_main(atype, false, false);
}
}
else
{
// uh-oh, we shouldn't have gotten here
llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
if (callback)
{
callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM);
}
}
}

View File

@@ -2,31 +2,25 @@
* @file llviewerassetstorage.h
* @brief Class for loading asset data to/from an external source.
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
* Copyright (C) 2010, Linden Research, Inc.
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,6 +28,7 @@
#define LLVIEWERASSETSTORAGE_H
#include "llassetstorage.h"
//#include "curl/curl.h"
class LLVFile;
@@ -68,8 +63,17 @@ public:
bool is_priority = false,
bool user_waiting=FALSE,
F64 timeout=LL_ASSET_STORAGE_TIMEOUT);
protected:
using LLAssetStorage::_queueDataRequest;
// virtual
void _queueDataRequest(const LLUUID& uuid,
LLAssetType::EType type,
void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat),
void *user_data,
BOOL duplicate,
BOOL is_priority);
};
#endif

View File

@@ -4175,15 +4175,15 @@ void LLViewerObject::setTEImage(const U8 te, LLViewerTexture *imagep)
}
}
S32 LLViewerObject::setTETextureCore(const U8 te, const LLUUID& uuid, const std::string &url )
S32 LLViewerObject::setTETextureCore(const U8 te, LLViewerTexture *image)
{
const LLUUID& uuid = image->getID();
S32 retval = 0;
if (uuid != getTE(te)->getID() ||
uuid == LLUUID::null)
{
retval = LLPrimitive::setTETexture(te, uuid);
mTEImages[te] = LLViewerTextureManager::getFetchedTextureFromUrl (url, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, uuid);
mTEImages[te] = image;
setChanged(TEXTURE);
if (mDrawable.notNull())
{
@@ -4193,24 +4193,7 @@ S32 LLViewerObject::setTETextureCore(const U8 te, const LLUUID& uuid, const std:
return retval;
}
S32 LLViewerObject::setTETextureCore(const U8 te, const LLUUID& uuid, LLHost host)
{
S32 retval = 0;
if (uuid != getTE(te)->getID() ||
uuid == LLUUID::null)
{
retval = LLPrimitive::setTETexture(te, uuid);
mTEImages[te] = LLViewerTextureManager::getFetchedTexture(uuid, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, host);
setChanged(TEXTURE);
if (mDrawable.notNull())
{
gPipeline.markTextured(mDrawable);
}
}
return retval;
}
//virtual
void LLViewerObject::changeTEImage(S32 index, LLViewerTexture* new_image)
{
if(index < 0 || index >= getNumTEs())
@@ -4223,7 +4206,9 @@ void LLViewerObject::changeTEImage(S32 index, LLViewerTexture* new_image)
S32 LLViewerObject::setTETexture(const U8 te, const LLUUID& uuid)
{
// Invalid host == get from the agent's sim
return setTETextureCore(te, uuid, LLHost::invalid);
LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture(
uuid, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, LLHost::invalid);
return setTETextureCore(te,image);
}

View File

@@ -307,8 +307,7 @@ public:
/*virtual*/ void setNumTEs(const U8 num_tes);
/*virtual*/ void setTE(const U8 te, const LLTextureEntry &texture_entry);
/*virtual*/ S32 setTETexture(const U8 te, const LLUUID &uuid);
S32 setTETextureCore(const U8 te, const LLUUID& uuid, LLHost host);
S32 setTETextureCore(const U8 te, const LLUUID& uuid, const std::string &url );
S32 setTETextureCore(const U8 te, LLViewerTexture *image);
/*virtual*/ S32 setTEColor(const U8 te, const LLColor3 &color);
/*virtual*/ S32 setTEColor(const U8 te, const LLColor4 &color);
/*virtual*/ S32 setTEScale(const U8 te, const F32 s, const F32 t);

View File

@@ -1558,6 +1558,17 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use
// Actually extract the data.
if (parcel)
{
if (sequence_id == SELECTED_PARCEL_SEQ_ID
&& parcel->getLocalID() != INVALID_PARCEL_ID
&& parcel->getLocalID() != local_id)
{
// The parcel has a valid parcel ID but it doesn't match the parcel
// for the data received.
llinfos << "Expecting data for parcel " << parcel->getLocalID() \
<< " but got data for parcel " << local_id << llendl;
return;
}
parcel->init(owner_id,
FALSE, FALSE, FALSE,
claim_date, claim_price_per_meter, rent_price_per_meter,

View File

@@ -742,7 +742,7 @@ void LLViewerRegion::processRegionInfo(LLMessageSystem* msg, void**)
// send it to 'observers'
// *TODO: switch the floaters to using LLRegionInfoModel
llinfos << "Processing region info" << llendl;
LLRegionInfoModel::getInstance()->update(msg);
LLRegionInfoModel::instance().update(msg);
LLFloaterGodTools::processRegionInfo(msg);
LLFloaterRegionInfo::processRegionInfo(msg);
LLFloaterReporter::processRegionInfo(msg);
@@ -1660,7 +1660,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("UpdateScriptAgent");
capabilityNames.append("UpdateScriptTask");
capabilityNames.append("UploadBakedTexture");
//capabilityNames.append("ViewerMetrics");
capabilityNames.append("ViewerMetrics");
capabilityNames.append("ViewerStartAuction");
capabilityNames.append("ViewerStats");

View File

@@ -881,3 +881,72 @@ void send_stats()
LLHTTPClient::post(url, body, new ViewerStatsResponder);
}
LLViewerStats::PhaseMap::PhaseMap()
{
}
LLTimer& LLViewerStats::PhaseMap::getPhaseTimer(const std::string& phase_name)
{
phase_map_t::iterator iter = mPhaseMap.find(phase_name);
if (iter == mPhaseMap.end())
{
LLTimer timer;
mPhaseMap[phase_name] = timer;
}
LLTimer& timer = mPhaseMap[phase_name];
return timer;
}
void LLViewerStats::PhaseMap::startPhase(const std::string& phase_name)
{
LLTimer& timer = getPhaseTimer(phase_name);
lldebugs << "startPhase " << phase_name << llendl;
timer.start();
}
void LLViewerStats::PhaseMap::stopPhase(const std::string& phase_name)
{
phase_map_t::iterator iter = mPhaseMap.find(phase_name);
if (iter != mPhaseMap.end())
{
if (iter->second.getStarted())
{
// Going from started to paused state - record stats.
iter->second.stop();
}
}
}
bool LLViewerStats::PhaseMap::getPhaseValues(const std::string& phase_name, F32& elapsed, bool& completed)
{
phase_map_t::iterator iter = mPhaseMap.find(phase_name);
if (iter != mPhaseMap.end())
{
elapsed = iter->second.getElapsedTimeF32();
completed = !iter->second.getStarted();
return true;
}
else
{
return false;
}
}
void LLViewerStats::PhaseMap::clearPhases()
{
lldebugs << "clearPhases" << llendl;
mPhaseMap.clear();
}
LLSD LLViewerStats::PhaseMap::dumpPhases()
{
LLSD result;
for (phase_map_t::iterator iter = mPhaseMap.begin(); iter != mPhaseMap.end(); ++iter)
{
const std::string& phase_name = iter->first;
result[phase_name]["completed"] = LLSD::Integer(!(iter->second.getStarted()));
result[phase_name]["elapsed"] = iter->second.getElapsedTimeF32();
}
return result;
}

View File

@@ -285,6 +285,26 @@ public:
StatsAccumulator mAgentPositionSnaps;
// Phase tracking (originally put in for avatar rezzing), tracking
// progress of active/completed phases for activities like outfit changing.
typedef std::map<std::string,LLTimer> phase_map_t;
typedef std::map<std::string,StatsAccumulator> phase_stats_t;
class PhaseMap
{
private:
phase_map_t mPhaseMap;
public:
PhaseMap();
LLTimer& getPhaseTimer(const std::string& phase_name);
bool getPhaseValues(const std::string& phase_name, F32& elapsed, bool& completed);
void startPhase(const std::string& phase_name);
void stopPhase(const std::string& phase_name);
void clearPhases();
LLSD dumpPhases();
phase_map_t::iterator begin() { return mPhaseMap.begin(); }
phase_map_t::iterator end() { return mPhaseMap.end(); }
};
private:
F64 mStats[ST_COUNT];

View File

@@ -1137,7 +1137,7 @@ void LLViewerFetchedTexture::dump()
// ONLY called from LLViewerFetchedTextureList
void LLViewerFetchedTexture::destroyTexture()
{
if(LLImageGL::sGlobalTextureMemoryInBytes < sMaxDesiredTextureMemInBytes)//not ready to release unused memory.
if(LLImageGL::sGlobalTextureMemoryInBytes < sMaxDesiredTextureMemInBytes * 0.95f)//not ready to release unused memory.
{
return ;
}

View File

@@ -137,6 +137,7 @@ public:
void resetTextureStats();
void setMaxVirtualSizeResetInterval(S32 interval)const {mMaxVirtualSizeResetInterval = interval;}
void resetMaxVirtualSizeResetCounter()const {mMaxVirtualSizeResetCounter = mMaxVirtualSizeResetInterval;}
S32 getMaxVirtualSizeResetCounter() const { return mMaxVirtualSizeResetCounter; }
virtual F32 getMaxVirtualSize() ;
@@ -309,6 +310,7 @@ public:
// the priority list, and cause horrible things to happen.
void setDecodePriority(F32 priority = -1.0f);
F32 getDecodePriority() const { return mDecodePriority; };
F32 getAdditionalDecodePriority() const { return mAdditionalDecodePriority; };
void setAdditionalDecodePriority(F32 priority) ;

View File

@@ -400,6 +400,23 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string&
LLPointer<LLViewerFetchedTexture> imagep = findImage(new_id);
if (!imagep.isNull())
{
LLViewerFetchedTexture *texture = imagep.get();
if (texture->getUrl().empty())
{
llwarns << "Requested texture " << new_id << " already exists but does not have a URL" << llendl;
}
else if (texture->getUrl() != url)
{
// This is not an error as long as the images really match -
// e.g. could be two avatars wearing the same outfit.
LL_DEBUGS("Avatar") << "Requested texture " << new_id
<< " already exists with a different url, requested: " << url
<< " current: " << texture->getUrl() << llendl;
}
}
if (imagep.isNull())
{
switch(texture_type)
@@ -461,7 +478,23 @@ LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id,
}
LLPointer<LLViewerFetchedTexture> imagep = findImage(image_id);
if (!imagep.isNull())
{
LLViewerFetchedTexture *texture = imagep.get();
if (request_from_host.isOk() &&
!texture->getTargetHost().isOk())
{
llwarns << "Requested texture " << image_id << " already exists but does not have a host" << llendl;
}
else if (request_from_host.isOk() &&
texture->getTargetHost().isOk() &&
request_from_host != texture->getTargetHost())
{
llwarns << "Requested texture " << image_id << " already exists with a different target host, requested: "
<< request_from_host << " current: " << texture->getTargetHost() << llendl;
}
}
if (imagep.isNull())
{
imagep = createImage(image_id, usemipmaps, boost_priority, texture_type, internal_format, primary_format, request_from_host) ;
@@ -531,6 +564,7 @@ LLViewerFetchedTexture *LLViewerTextureList::findImage(const LLUUID &image_id)
void LLViewerTextureList::addImageToList(LLViewerFetchedTexture *image)
{
assert_main_thread();
llassert_always(mInitialized) ;
llassert(image);
if (image->isInImageList())
@@ -547,6 +581,7 @@ void LLViewerTextureList::addImageToList(LLViewerFetchedTexture *image)
void LLViewerTextureList::removeImageFromList(LLViewerFetchedTexture *image)
{
assert_main_thread();
llassert_always(mInitialized) ;
llassert(image);
if (!image->isInImageList())
@@ -900,8 +935,7 @@ void LLViewerTextureList::forceImmediateUpdate(LLViewerFetchedTexture* imagep)
imagep->processTextureStats();
F32 decode_priority = LLViewerFetchedTexture::maxDecodePriority() ;
imagep->setDecodePriority(decode_priority);
mImageList.insert(imagep);
imagep->setInImageList(TRUE) ;
addImageToList(imagep);
return ;
}
@@ -1022,8 +1056,7 @@ void LLViewerTextureList::decodeAllImages(F32 max_time)
imagep->processTextureStats();
F32 decode_priority = imagep->calcDecodePriority();
imagep->setDecodePriority(decode_priority);
mImageList.insert(imagep);
imagep->setInImageList(TRUE) ;
addImageToList(imagep);
}
image_list.clear();

View File

@@ -1129,6 +1129,8 @@ LLVOAvatar::~LLVOAvatar()
mAnimationSources.clear();
LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ;
getPhases().clearPhases();
SHClientTagMgr::instance().clearAvatarTag(this);
lldebugs << "LLVOAvatar Destructor end" << llendl;
@@ -2193,27 +2195,53 @@ U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys,
return retval;
}
// virtual
S32 LLVOAvatar::setTETexture(const U8 te, const LLUUID& uuid)
LLViewerFetchedTexture *LLVOAvatar::getBakedTextureImage(const U8 te, const LLUUID& uuid)
{
// The core setTETexture() method requests images, so we need
// to redirect certain avatar texture requests to different sims.
if (isIndexBakedTexture((ETextureIndex)te))
LLViewerFetchedTexture *result = NULL;
if (uuid == IMG_DEFAULT_AVATAR ||
uuid == IMG_DEFAULT ||
uuid == IMG_INVISIBLE)
{
// Should already exist, don't need to find it on sim or baked-texture host.
result = gTextureList.findImage(uuid);
}
if (!result)
{
const std::string url = getImageURL(te,uuid);
if (!url.empty())
{
llinfos << "texture URL " << url << llendl;
return setTETextureCore(te, uuid, url);
result = LLViewerTextureManager::getFetchedTextureFromUrl(
url, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, uuid);
}
else
{
llinfos << "get texture from host " << uuid << llendl;
LLHost host = getObjectHost();
result = LLViewerTextureManager::getFetchedTexture(
uuid, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE, 0, 0, host);
}
}
return result;
}
LLHost target_host = getObjectHost();
return setTETextureCore(te, uuid, target_host);
}
else
// virtual
S32 LLVOAvatar::setTETexture(const U8 te, const LLUUID& uuid)
{
if (!isIndexBakedTexture((ETextureIndex)te))
{
return setTETextureCore(te, uuid, LLHost::invalid);
if (!uuid.isNull())
{
llinfos << "ignoring texture " << uuid << " in non-baked slot " << (S32)te << " - will use null " << llendl;
}
return LLViewerObject::setTETexture(te, LLUUID::null);
}
LLViewerFetchedTexture *image = getBakedTextureImage(te,uuid);
llassert(image);
return setTETextureCore(te, image);
}
static LLFastTimer::DeclareTimer FTM_AVATAR_UPDATE("Avatar Update");
@@ -3536,9 +3564,24 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
{
central_bake_version = getRegion()->getCentralBakeVersion();
}
addDebugText(llformat("mLocal: %d, mEdit: %d, mUSB: %d, CBV: %d",
mUseLocalAppearance, mIsEditingAppearance,
mUseServerBakes, central_bake_version));
bool all_baked_downloaded = allBakedTexturesCompletelyDownloaded();
bool all_local_downloaded = allLocalTexturesCompletelyDownloaded();
std::string debug_line = llformat("%s%s - mLocal: %d, mEdit: %d, mUSB: %d, CBV: %d",
all_local_downloaded ? "L" : "l",
all_baked_downloaded ? "B" : "b",
mUseLocalAppearance, mIsEditingAppearance,
mUseServerBakes, central_bake_version);
std::string origin_string = bakedTextureOriginInfo();
debug_line += " [" + origin_string + "]";
if (isSelf())
{
S32 curr_cof_version = LLAppearanceMgr::instance().getCOFVersion();
S32 last_request_cof_version = LLAppearanceMgr::instance().getLastUpdateRequestCOFVersion();
S32 last_received_cof_version = LLAppearanceMgr::instance().getLastAppearanceUpdateCOFVersion();
debug_line += llformat(" - cof: %d req: %d rcv:%d",
curr_cof_version, last_request_cof_version, last_received_cof_version);
}
addDebugText(debug_line);
}
if (gSavedSettings.getBOOL("DebugAvatarCompositeBaked"))
{
@@ -4675,11 +4718,206 @@ U32 LLVOAvatar::renderImpostor(LLColor4U color, S32 diffuse_channel)
return 6;
}
//------------------------------------------------------------------------
// LLVOAvatar::updateTextures()
//------------------------------------------------------------------------
bool LLVOAvatar::allTexturesCompletelyDownloaded(std::set<LLUUID>& ids)
{
for (std::set<LLUUID>::const_iterator it = ids.begin(); it != ids.end(); ++it)
{
LLViewerFetchedTexture *imagep = gTextureList.findImage(*it);
if (imagep && imagep->getDiscardLevel()!=0)
{
return false;
}
}
return true;
}
bool LLVOAvatar::allLocalTexturesCompletelyDownloaded()
{
std::set<LLUUID> local_ids;
collectLocalTextureUUIDs(local_ids);
return allTexturesCompletelyDownloaded(local_ids);
}
bool LLVOAvatar::allBakedTexturesCompletelyDownloaded()
{
std::set<LLUUID> baked_ids;
collectBakedTextureUUIDs(baked_ids);
return allTexturesCompletelyDownloaded(baked_ids);
}
void LLVOAvatar::bakedTextureOriginCounts(S32 &sb_count, // server-bake, has origin URL.
S32 &host_count, // host-based bake, has host.
S32 &both_count, // error - both host and URL set.
S32 &neither_count) // error - neither set.
{
sb_count = host_count = both_count = neither_count = 0;
std::set<LLUUID> baked_ids;
collectBakedTextureUUIDs(baked_ids);
for (std::set<LLUUID>::const_iterator it = baked_ids.begin(); it != baked_ids.end(); ++it)
{
LLViewerFetchedTexture *imagep = gTextureList.findImage(*it);
bool has_url = false, has_host = false;
if (!imagep->getUrl().empty())
{
has_url = true;
}
if (imagep->getTargetHost().isOk())
{
has_host = true;
}
if (has_url && !has_host) sb_count++;
else if (has_host && !has_url) host_count++;
else if (has_host && has_url) both_count++;
else if (!has_host && !has_url) neither_count++;
}
}
std::string LLVOAvatar::bakedTextureOriginInfo()
{
std::string result;
std::set<LLUUID> baked_ids;
collectBakedTextureUUIDs(baked_ids);
for (std::set<LLUUID>::const_iterator it = baked_ids.begin(); it != baked_ids.end(); ++it)
{
LLViewerFetchedTexture *imagep = gTextureList.findImage(*it);
bool has_url = false, has_host = false;
if (!imagep->getUrl().empty())
{
has_url = true;
}
if (imagep->getTargetHost().isOk())
{
has_host = true;
}
if (has_url && !has_host) result += "u"; // server-bake texture with url
else if (has_host && !has_url) result += "h"; // old-style texture on sim
else if (has_host && has_url) result += "?"; // both origins?
else if (!has_host && !has_url) result += "n"; // no origin?
}
return result;
}
S32 LLVOAvatar::totalTextureMemForUUIDS(std::set<LLUUID>& ids)
{
S32 result = 0;
for (std::set<LLUUID>::const_iterator it = ids.begin(); it != ids.end(); ++it)
{
LLViewerFetchedTexture *imagep = gTextureList.findImage(*it);
if (imagep)
{
result += imagep->getTextureMemory();
}
}
return result;
}
void LLVOAvatar::collectLocalTextureUUIDs(std::set<LLUUID>& ids)
{
for (U32 texture_index = 0; texture_index < getNumTEs(); texture_index++)
{
LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType((ETextureIndex)texture_index);
U32 num_wearables = gAgentWearables.getWearableCount(wearable_type);
LLViewerFetchedTexture *imagep = NULL;
for (U32 wearable_index = 0; wearable_index < num_wearables; wearable_index++)
{
imagep = LLViewerTextureManager::staticCastToFetchedTexture(getImage(texture_index, wearable_index), TRUE);
if (imagep)
{
const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = LLAvatarAppearanceDictionary::getInstance()->getTexture((ETextureIndex)texture_index);
if (texture_dict->mIsLocalTexture)
{
ids.insert(imagep->getID());
}
}
}
}
ids.erase(IMG_DEFAULT);
ids.erase(IMG_DEFAULT_AVATAR);
ids.erase(IMG_INVISIBLE);
}
void LLVOAvatar::collectBakedTextureUUIDs(std::set<LLUUID>& ids)
{
for (U32 texture_index = 0; texture_index < getNumTEs(); texture_index++)
{
LLViewerFetchedTexture *imagep = NULL;
if (isIndexBakedTexture((ETextureIndex) texture_index))
{
imagep = LLViewerTextureManager::staticCastToFetchedTexture(getImage(texture_index,0), TRUE);
if (imagep)
{
ids.insert(imagep->getID());
}
}
}
ids.erase(IMG_DEFAULT);
ids.erase(IMG_DEFAULT_AVATAR);
ids.erase(IMG_INVISIBLE);
}
void LLVOAvatar::collectTextureUUIDs(std::set<LLUUID>& ids)
{
collectLocalTextureUUIDs(ids);
collectBakedTextureUUIDs(ids);
}
void LLVOAvatar::releaseOldTextures()
{
S32 current_texture_mem = 0;
// Any textures that we used to be using but are no longer using should no longer be flagged as "NO_DELETE"
std::set<LLUUID> baked_texture_ids;
collectBakedTextureUUIDs(baked_texture_ids);
S32 new_baked_mem = totalTextureMemForUUIDS(baked_texture_ids);
std::set<LLUUID> local_texture_ids;
collectLocalTextureUUIDs(local_texture_ids);
S32 new_local_mem = totalTextureMemForUUIDS(local_texture_ids);
std::set<LLUUID> new_texture_ids;
new_texture_ids.insert(baked_texture_ids.begin(),baked_texture_ids.end());
new_texture_ids.insert(local_texture_ids.begin(),local_texture_ids.end());
S32 new_total_mem = totalTextureMemForUUIDS(new_texture_ids);
S32 old_total_mem = totalTextureMemForUUIDS(mTextureIDs);
LL_DEBUGS("Avatar") << getFullname() << " old_total_mem: " << old_total_mem << " new_total_mem (L/B): " << new_total_mem << " (" << new_local_mem <<", " << new_baked_mem << ")" << llendl;
if (!isSelf() && new_total_mem > new_baked_mem)
{
llwarns << "extra local textures stored for non-self av" << llendl;
}
for (std::set<LLUUID>::iterator it = mTextureIDs.begin(); it != mTextureIDs.end(); ++it)
{
if (new_texture_ids.find(*it) == new_texture_ids.end())
{
LLViewerFetchedTexture *imagep = gTextureList.findImage(*it);
if (imagep)
{
current_texture_mem += imagep->getTextureMemory();
if (imagep->getTextureState() == LLGLTexture::NO_DELETE)
{
// This will allow the texture to be deleted if not in use.
imagep->forceActive();
// This resets the clock to texture being flagged
// as unused, preventing the texture from being
// deleted immediately. If other avatars or
// objects are using it, it can still be flagged
// no-delete by them.
imagep->forceUpdateBindStats();
}
}
}
}
mTextureIDs = new_texture_ids;
}
void LLVOAvatar::updateTextures()
{
releaseOldTextures();
BOOL render_avatar = TRUE;
if (mIsDummy || gNoRender)
@@ -6604,39 +6842,141 @@ void LLVOAvatar::updateRezzedStatusTimers()
if (rez_status != mLastRezzedStatus)
{
LL_DEBUGS("Avatar") << avString() << "rez state change: " << mLastRezzedStatus << " -> " << rez_status << LL_ENDL;
/*bool is_cloud_or_gray = (rez_status==0 || rez_status==1);
bool is_cloud_or_gray = (rez_status==0 || rez_status==1);
bool was_cloud_or_gray = (mLastRezzedStatus==0 || mLastRezzedStatus==1);
bool is_cloud = (rez_status==0);
bool was_cloud = (mLastRezzedStatus==0);
]// Non-cloud to cloud
// Non-cloud to cloud
if (is_cloud && !was_cloud)
{
// start cloud timer.
getPhases().startPhase("cloud");
startPhase("cloud");
}
else if (was_cloud && !is_cloud)
{
// stop cloud timer, which will capture stats.
getPhases().stopPhase("cloud");
stopPhase("cloud");
}
// Non-cloud-or-gray to cloud-or-gray
if (is_cloud_or_gray && !was_cloud_or_gray)
{
// start cloud-or-gray timer.
getPhases().startPhase("cloud-or-gray");
startPhase("cloud-or-gray");
}
else if (was_cloud_or_gray && !is_cloud_or_gray)
{
// stop cloud-or-gray timer, which will capture stats.
getPhases().stopPhase("cloud-or-gray");
}*/
stopPhase("cloud-or-gray");
}
mLastRezzedStatus = rez_status;
}
}
void LLVOAvatar::clearPhases()
{
getPhases().clearPhases();
}
void LLVOAvatar::startPhase(const std::string& phase_name)
{
getPhases().startPhase(phase_name);
}
void LLVOAvatar::logPendingPhases()
{
for (LLViewerStats::phase_map_t::iterator it = getPhases().begin();
it != getPhases().end();
++it)
{
const std::string& phase_name = it->first;
F32 elapsed;
bool completed;
if (getPhases().getPhaseValues(phase_name, elapsed, completed))
{
if (!completed)
{
logMetricsTimerRecord(phase_name, elapsed, completed);
}
else
{
llwarns << "ignoring " << phase_name << llendl;
}
}
}
}
//static
void LLVOAvatar::logPendingPhasesAllAvatars()
{
for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
iter != LLCharacter::sInstances.end(); ++iter)
{
LLVOAvatar* inst = (LLVOAvatar*) *iter;
if( inst->isDead() )
{
continue;
}
inst->logPendingPhases();
}
}
void LLVOAvatar::logMetricsTimerRecord(const std::string& phase_name, F32 elapsed, bool completed)
{
LLSD record;
record["timer_name"] = phase_name;
record["agent_id"] = gAgent.getID();
record["avatar_id"] = getID();
record["elapsed"] = elapsed;
record["completed"] = completed;
U32 grid_x(0), grid_y(0);
if (getRegion())
{
record["central_bake_version"] = LLSD::Integer(getRegion()->getCentralBakeVersion());
grid_from_region_handle(getRegion()->getHandle(), &grid_x, &grid_y);
}
record["grid_x"] = LLSD::Integer(grid_x);
record["grid_y"] = LLSD::Integer(grid_y);
record["is_using_server_bakes"] = ((bool) isUsingServerBakes());
record["is_self"] = isSelf();
std::ostringstream ostr;
ostr << LLSDNotationStreamer(record);
LL_DEBUGS("Avatar") << "record\n" << ostr.str() << llendl;
if (isAgentAvatarValid())
{
gAgentAvatarp->addMetricsTimerRecord(record);
}
}
void LLVOAvatar::stopPhase(const std::string& phase_name)
{
F32 elapsed;
bool completed;
if (getPhases().getPhaseValues(phase_name, elapsed, completed))
{
if (!completed)
{
getPhases().stopPhase(phase_name);
completed = true;
}
else
{
llwarns << "stop when stopped already for " << phase_name << llendl;
}
logMetricsTimerRecord(phase_name, elapsed, completed);
}
else
{
llwarns << "stop when not started for " << phase_name << llendl;
}
}
// call periodically to keep isFullyLoaded up to date.
// returns true if the value has changed.
BOOL LLVOAvatar::updateIsFullyLoaded()
@@ -6764,6 +7104,7 @@ void LLVOAvatar::debugColorizeSubMeshes(U32 i, const LLColor4& color)
// virtual
void LLVOAvatar::updateMeshTextures()
{
static S32 update_counter = 0;
mBakedTextureDebugText.clear();
// llinfos << "updateMeshTextures" << llendl;
if (gNoRender) return;
@@ -6794,7 +7135,8 @@ void LLVOAvatar::updateMeshTextures()
std::vector<BOOL> use_lkg_baked_layer; // lkg = "last known good"
use_lkg_baked_layer.resize(mBakedTextureDatas.size(), false);
mBakedTextureDebugText += "indx layerset linvld ltda ilb ulkg ltid\n";
mBakedTextureDebugText += llformat("%06d\n",update_counter++);
mBakedTextureDebugText += "indx layerset linvld ltda ilb ulkg ltid\n";
for (U32 i=0; i < mBakedTextureDatas.size(); i++)
{
is_layer_baked[i] = isTextureDefined(mBakedTextureDatas[i].mTextureIndex);
@@ -6827,6 +7169,8 @@ void LLVOAvatar::updateMeshTextures()
last_id_string = "A";
else if (mBakedTextureDatas[i].mLastTextureID == IMG_DEFAULT)
last_id_string = "D";
else if (mBakedTextureDatas[i].mLastTextureID == IMG_INVISIBLE)
last_id_string = "I";
else
last_id_string = "*";
bool is_ltda = layerset
@@ -7370,7 +7714,7 @@ void LLVOAvatar::dumpAppearanceMsgParams( const std::string& dump_prefix,
}
else
{
llinfos << "xmlfile write handle obtained : " << fullpath << llendl;
LL_DEBUGS("Avatar") << "dumping appearance message to " << fullpath << llendl;
}
@@ -7394,47 +7738,174 @@ void LLVOAvatar::dumpAppearanceMsgParams( const std::string& dump_prefix,
}
}
struct LLAppearanceMessageContents
{
LLAppearanceMessageContents():
mAppearanceVersion(-1),
mParamAppearanceVersion(-1),
mCOFVersion(LLViewerInventoryCategory::VERSION_UNKNOWN)
{
}
LLTEContents mTEContents;
S32 mAppearanceVersion;
S32 mParamAppearanceVersion;
S32 mCOFVersion;
// For future use:
//U32 appearance_flags = 0;
std::vector<F32> mParamWeights;
std::vector<LLVisualParam*> mParams;
};
void LLVOAvatar::parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMessageContents& contents)
{
parseTEMessage(mesgsys, _PREHASH_ObjectData, -1, contents.mTEContents);
// Parse the AppearanceData field, if any.
if (mesgsys->has(_PREHASH_AppearanceData))
{
U8 av_u8;
mesgsys->getU8Fast(_PREHASH_AppearanceData, _PREHASH_AppearanceVersion, av_u8, 0);
contents.mAppearanceVersion = av_u8;
llinfos << "appversion set by AppearanceData field: " << contents.mAppearanceVersion << llendl;
mesgsys->getS32Fast(_PREHASH_AppearanceData, _PREHASH_CofVersion, contents.mCOFVersion, 0);
// For future use:
//mesgsys->getU32Fast(_PREHASH_AppearanceData, _PREHASH_Flags, appearance_flags, 0);
}
// Parse visual params, if any.
S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_VisualParam);
bool drop_visual_params_debug = gSavedSettings.getBOOL("BlockSomeAvatarAppearanceVisualParams") && (ll_rand(2) == 0); // pretend that ~12% of AvatarAppearance messages arrived without a VisualParam block, for testing
if( num_blocks > 1 && !drop_visual_params_debug)
{
LL_DEBUGS("Avatar") << avString() << " handle visual params, num_blocks " << num_blocks << LL_ENDL;
LLVisualParam* param = getFirstVisualParam();
llassert(param); // if this ever fires, we should do the same as when num_blocks<=1
if (!param)
{
llwarns << "No visual params!" << llendl;
}
else
{
for( S32 i = 0; i < num_blocks; i++ )
{
while( param && (param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) ) // should not be any of group VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT
{
param = getNextVisualParam();
}
if( !param )
{
// more visual params supplied than expected - just process what we know about
break;
}
U8 value;
mesgsys->getU8Fast(_PREHASH_VisualParam, _PREHASH_ParamValue, value, i);
F32 newWeight = U8_to_F32(value, param->getMinWeight(), param->getMaxWeight());
contents.mParamWeights.push_back(newWeight);
contents.mParams.push_back(param);
param = getNextVisualParam();
}
}
const S32 expected_tweakable_count = getVisualParamCountInGroup(VISUAL_PARAM_GROUP_TWEAKABLE); // don't worry about VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT
if (num_blocks != expected_tweakable_count)
{
llinfos << "Number of params in AvatarAppearance msg (" << num_blocks << ") does not match number of tweakable params in avatar xml file (" << expected_tweakable_count << "). Processing what we can. object: " << getID() << llendl;
}
}
else
{
if (drop_visual_params_debug)
{
llinfos << "Debug-faked lack of parameters on AvatarAppearance for object: " << getID() << llendl;
}
else
{
llinfos << "AvatarAppearance msg received without any parameters, object: " << getID() << llendl;
}
}
LLVisualParam* appearance_version_param = getVisualParam(11000);
if (appearance_version_param)
{
std::vector<LLVisualParam*>::iterator it = std::find(contents.mParams.begin(), contents.mParams.end(),appearance_version_param);
if (it != contents.mParams.end())
{
S32 index = it - contents.mParams.begin();
llinfos << "index: " << index << llendl;
contents.mParamAppearanceVersion = llround(contents.mParamWeights[index]);
LL_DEBUGS("Avatar") << "appversion req by appearance_version param: " << contents.mParamAppearanceVersion << llendl;
}
}
}
bool resolve_appearance_version(const LLAppearanceMessageContents& contents, S32& appearance_version)
{
appearance_version = -1;
if ((contents.mAppearanceVersion) >= 0 &&
(contents.mParamAppearanceVersion >= 0) &&
(contents.mAppearanceVersion != contents.mParamAppearanceVersion))
{
llwarns << "inconsistent appearance_version settings - field: " <<
contents.mAppearanceVersion << ", param: " << contents.mParamAppearanceVersion << llendl;
return false;
}
if (contents.mParamAppearanceVersion >= 0) // use visual param if available.
{
appearance_version = contents.mParamAppearanceVersion;
}
if (contents.mAppearanceVersion >= 0)
{
appearance_version = contents.mAppearanceVersion;
}
if (appearance_version < 0) // still not set, go with 0.
{
appearance_version = 0;
}
LL_DEBUGS("Avatar") << "appearance version info - field " << contents.mAppearanceVersion
<< " param: " << contents.mParamAppearanceVersion
<< " final: " << appearance_version << llendl;
return true;
}
//-----------------------------------------------------------------------------
// processAvatarAppearance()
//-----------------------------------------------------------------------------
void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
{
LL_DEBUGS("Avatar") << "starts" << llendl;
bool enable_verbose_dumps = gSavedSettings.getBOOL("DebugAvatarAppearanceMessage");
std::string dump_prefix = getFullname() + "_" + (isSelf()?"s":"o") + "_";
if (enable_verbose_dumps) { dumpArchetypeXML(dump_prefix + "process_start"); }
//if (enable_verbose_dumps) { dumpArchetypeXML(dump_prefix + "process_start"); }
if (gSavedSettings.getBOOL("BlockAvatarAppearanceMessages"))
{
llwarns << "Blocking AvatarAppearance message" << llendl;
return;
}
LLMemType mt(LLMemType::MTYPE_AVATAR);
BOOL is_first_appearance_message = !mFirstAppearanceMessageReceived;
mFirstAppearanceMessageReceived = TRUE;
LL_INFOS("Avatar") << avString() << "processAvatarAppearance start " << mID
<< " first? " << is_first_appearance_message << " self? " << isSelf() << LL_ENDL;
ESex old_sex = getSex();
LLTEContents tec;
parseTEMessage(mesgsys, _PREHASH_ObjectData, -1, tec);
U8 appearance_version = 0;
S32 this_update_cof_version = LLViewerInventoryCategory::VERSION_UNKNOWN;
S32 last_update_request_cof_version = LLAppearanceMgr::instance().mLastUpdateRequestCOFVersion;
// For future use:
//U32 appearance_flags = 0;
if (mesgsys->has(_PREHASH_AppearanceData))
LLAppearanceMessageContents contents;
parseAppearanceMessage(mesgsys, contents);
if (enable_verbose_dumps)
{
mesgsys->getU8Fast(_PREHASH_AppearanceData, _PREHASH_AppearanceVersion, appearance_version, 0);
mesgsys->getS32Fast(_PREHASH_AppearanceData, _PREHASH_CofVersion, this_update_cof_version, 0);
// For future use:
//mesgsys->getU32Fast(_PREHASH_AppearanceData, _PREHASH_Flags, appearance_flags, 0);
dumpAppearanceMsgParams(dump_prefix + "appearance_msg", contents.mParamWeights, contents.mTEContents);
}
S32 appearance_version;
if (!resolve_appearance_version(contents, appearance_version))
{
llwarns << "bad appearance version info, discarding" << llendl;
return;
}
S32 this_update_cof_version = contents.mCOFVersion;
S32 last_update_request_cof_version = LLAppearanceMgr::instance().mLastUpdateRequestCOFVersion;
// Only now that we have result of appearance_version can we decide whether to bail out.
if( isSelf() )
{
@@ -7442,6 +7913,8 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
<< " last_update_request_cof_version " << last_update_request_cof_version
<< " my_cof_version " << LLAppearanceMgr::instance().getCOFVersion() << llendl;
LLAppearanceMgr::instance().setLastAppearanceUpdateCOFVersion(this_update_cof_version);
if (getRegion() && (getRegion()->getCentralBakeVersion()==0))
{
llwarns << avString() << "Received AvatarAppearance message for self in non-server-bake region" << llendl;
@@ -7451,6 +7924,10 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
return;
}
}
else
{
LL_DEBUGS("Avatar") << "appearance message received" << llendl;
}
if (gNoRender)
{
@@ -7473,9 +7950,20 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
return;
}
mUseServerBakes = (appearance_version > 0);
S32 num_params = contents.mParamWeights.size();
if (num_params <= 1)
{
// In this case, we have no reliable basis for knowing
// appearance version, which may cause us to look for baked
// textures in the wrong place and flag them as missing
// assets.
llinfos << "ignoring appearance message due to lack of params" << llendl;
return;
}
applyParsedTEMessage(tec);
setIsUsingServerBakes(appearance_version > 0);
applyParsedTEMessage(contents.mTEContents);
SHClientTagMgr::instance().updateAvatarTag(this);
@@ -7493,16 +7981,16 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
}
}
// runway - was
// if (!is_first_appearance_message )
// which means it would be called on second appearance message - probably wrong.
BOOL is_first_appearance_message = !mFirstAppearanceMessageReceived;
mFirstAppearanceMessageReceived = TRUE;
//llinfos << "Received AvatarAppearance: " << (isSelf() ? "(self): " : "(other): ") << std::endl <<
// (isTextureDefined(TEX_HEAD_BAKED) ? "HEAD " : "head " ) << (getTEImage(TEX_HEAD_BAKED)->getID()) << std::endl <<
// (isTextureDefined(TEX_UPPER_BAKED) ? "UPPER " : "upper " ) << (getTEImage(TEX_UPPER_BAKED)->getID()) << std::endl <<
// (isTextureDefined(TEX_LOWER_BAKED) ? "LOWER " : "lower " ) << (getTEImage(TEX_LOWER_BAKED)->getID()) << std::endl <<
// (isTextureDefined(TEX_SKIRT_BAKED) ? "SKIRT " : "skirt " ) << (getTEImage(TEX_SKIRT_BAKED)->getID()) << std::endl <<
// (isTextureDefined(TEX_HAIR_BAKED) ? "HAIR" : "hair " ) << (getTEImage(TEX_HAIR_BAKED)->getID()) << std::endl <<
// (isTextureDefined(TEX_EYES_BAKED) ? "EYES" : "eyes" ) << (getTEImage(TEX_EYES_BAKED)->getID()) << llendl ;
LL_INFOS("Avatar") << avString() << "processAvatarAppearance start " << mID
<< " first? " << is_first_appearance_message << " self? " << isSelf() << LL_ENDL;
if( is_first_appearance_message )
if (is_first_appearance_message )
{
onFirstTEMessageReceived();
}
@@ -7512,85 +8000,45 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
mSupportsPhysics = false;
// parse visual params
S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_VisualParam);
bool drop_visual_params_debug = gSavedSettings.getBOOL("BlockSomeAvatarAppearanceVisualParams") && (ll_rand(2) == 0); // pretend that ~12% of AvatarAppearance messages arrived without a VisualParam block, for testing
if( num_blocks > 1 && !drop_visual_params_debug)
// Apply visual params
if( num_params > 1)
{
LL_DEBUGS("Avatar") << avString() << " handle visual params, num_blocks " << num_blocks << LL_ENDL;
LL_DEBUGS("Avatar") << avString() << " handle visual params, num_params " << num_params << LL_ENDL;
BOOL params_changed = FALSE;
BOOL interp_params = FALSE;
LLVisualParam* param = getFirstVisualParam();
llassert(param); // if this ever fires, we should do the same as when num_blocks<=1
if (!param)
for( S32 i = 0; i < num_params; i++ )
{
llwarns << "No visual params!" << llendl;
}
else
{
std::vector<F32> params_for_dump;
for( S32 i = 0; i < num_blocks; i++ )
LLVisualParam* param = contents.mParams[i];
F32 newWeight = contents.mParamWeights[i];
if(param->getID() == 10000)
{
while( param && (param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) ) // should not be any of group VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT
{
param = getNextVisualParam();
}
if( !param )
{
// more visual params supplied than expected - just process what we know about
break;
}
U8 value;
mesgsys->getU8Fast(_PREHASH_VisualParam, _PREHASH_ParamValue, value, i);
F32 newWeight = U8_to_F32(value, param->getMinWeight(), param->getMaxWeight());
if(param->getID() == 10000)
{
mSupportsPhysics = true;
}
else if(param->getID() == EMERALD_BOOB_GRAVITY_PARAM && newWeight != getActualBoobGrav())
{
setActualBoobGrav(newWeight);
}
params_for_dump.push_back(newWeight);
if (is_first_appearance_message || (param->getWeight() != newWeight))
{
//llinfos << "Received update for param " << param->getDisplayName() << " at value " << newWeight << llendl;
params_changed = TRUE;
if(is_first_appearance_message)
{
param->setWeight(newWeight, FALSE);
}
else
{
interp_params = TRUE;
param->setAnimationTarget(newWeight, FALSE);
}
}
param = getNextVisualParam();
mSupportsPhysics = true;
}
else if(param->getID() == EMERALD_BOOB_GRAVITY_PARAM && newWeight != getActualBoobGrav())
{
setActualBoobGrav(newWeight);
}
if (is_first_appearance_message || (param->getWeight() != newWeight))
{
params_changed = TRUE;
if(is_first_appearance_message)
{
param->setWeight(newWeight, FALSE);
}
else
{
interp_params = TRUE;
param->setAnimationTarget(newWeight, FALSE);
}
}
if (enable_verbose_dumps)
dumpAppearanceMsgParams(dump_prefix + "appearance_msg", params_for_dump, tec);
}
if (enable_verbose_dumps) { dumpArchetypeXML(dump_prefix + "process_post_set_weights"); }
const S32 expected_tweakable_count = getVisualParamCountInGroup(VISUAL_PARAM_GROUP_TWEAKABLE); // don't worry about VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT
if (num_blocks != expected_tweakable_count)
if (num_params != expected_tweakable_count)
{
llinfos << "Number of params in AvatarAppearance msg (" << num_blocks << ") does not match number of tweakable params in avatar xml file (" << expected_tweakable_count << "). Processing what we can. object: " << getID() << llendl;
if(is_first_appearance_message)
{
while( param && (param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) )
param = getNextVisualParam();
if(param && param->getName() == "breast_physics_mass")
llinfos << getFullname() << " does not have avatar physics." << llendl;
}
llinfos << "Number of params in AvatarAppearance msg (" << num_params << ") does not match number of tweakable params in avatar xml file (" << expected_tweakable_count << "). Processing what we can. object: " << getID() << llendl;
}
if (params_changed)
@@ -7614,14 +8062,6 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
{
// AvatarAppearance message arrived without visual params
LL_DEBUGS("Avatar") << avString() << "no visual params" << LL_ENDL;
if (drop_visual_params_debug)
{
llinfos << "Debug-faked lack of parameters on AvatarAppearance for object: " << getID() << llendl;
}
else
{
llinfos << "AvatarAppearance msg received without any parameters, object: " << getID() << llendl;
}
const F32 LOADING_TIMEOUT_SECONDS = 60.f;
// this isn't really a problem if we already have a non-default shape
@@ -7651,8 +8091,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
updateMeshTextures();
if (enable_verbose_dumps) dumpArchetypeXML(dump_prefix + "process_end");
// llinfos << "processAvatarAppearance end " << mID << llendl;
//if (enable_verbose_dumps) dumpArchetypeXML(dump_prefix + "process_end");
}
// static
@@ -8124,9 +8563,29 @@ void LLVOAvatar::bodySizeChanged()
}
}
BOOL LLVOAvatar::isUsingServerBakes() const
{
#if 1
// Sanity check - visual param for appearance version should match mUseServerBakes
LLVisualParam* appearance_version_param = getVisualParam(11000);
llassert(appearance_version_param);
F32 wt = appearance_version_param->getWeight();
F32 expect_wt = mUseServerBakes ? 1.0 : 0.0;
if (!is_approx_equal(wt,expect_wt))
{
llwarns << "wt " << wt << " differs from expected " << expect_wt << llendl;
}
#endif
return mUseServerBakes;
}
void LLVOAvatar::setIsUsingServerBakes(BOOL newval)
{
mUseServerBakes = newval;
LLVisualParam* appearance_version_param = getVisualParam(11000);
llassert(appearance_version_param);
appearance_version_param->setWeight(newval ? 1.0 : 0.0, false);
}
// virtual

View File

@@ -50,6 +50,7 @@
#include "lldriverparam.h"
#include "llviewertexlayer.h"
#include "material_codes.h" // LL_MCODE_END
#include "llviewerstats.h"
#include "emeraldboobutils.h"
#include "llavatarname.h"
@@ -72,6 +73,7 @@ class LLHUDNameTag;
class LLHUDEffectSpiral;
class LLTexGlobalColor;
class LLViewerJoint;
struct LLAppearanceMessageContents;
class SHClientTagMgr : public LLSingleton<SHClientTagMgr>, public boost::signals2::trackable
{
@@ -173,9 +175,21 @@ public:
virtual void idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
/*virtual*/ BOOL updateLOD();
BOOL updateJointLODs();
void updateLODRiggedAttachments( void );
void updateLODRiggedAttachments( void );
/*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
S32 totalTextureMemForUUIDS(std::set<LLUUID>& ids);
bool allTexturesCompletelyDownloaded(std::set<LLUUID>& ids);
bool allLocalTexturesCompletelyDownloaded();
bool allBakedTexturesCompletelyDownloaded();
void bakedTextureOriginCounts(S32 &sb_count, S32 &host_count,
S32 &both_count, S32 &neither_count);
std::string bakedTextureOriginInfo();
void collectLocalTextureUUIDs(std::set<LLUUID>& ids);
void collectBakedTextureUUIDs(std::set<LLUUID>& ids);
void collectTextureUUIDs(std::set<LLUUID>& ids);
void releaseOldTextures();
/*virtual*/ void updateTextures();
LLViewerFetchedTexture* getBakedTextureImage(const U8 te, const LLUUID& uuid);
/*virtual*/ S32 setTETexture(const U8 te, const LLUUID& uuid); // If setting a baked texture, need to request it from a non-local sim.
/*virtual*/ void onShift(const LLVector4a& shift_vector);
/*virtual*/ U32 getPartitionType() const;
@@ -323,7 +337,17 @@ public:
bool isFrozenDead() const { return mFreezeTimeDead; }
S32 mLastRezzedStatus;
void startPhase(const std::string& phase_name);
void stopPhase(const std::string& phase_name);
void clearPhases();
void logPendingPhases();
static void logPendingPhasesAllAvatars();
void logMetricsTimerRecord(const std::string& phase_name, F32 elapsed, bool completed);
protected:
LLViewerStats::PhaseMap& getPhases() { return mPhases; }
BOOL updateIsFullyLoaded();
BOOL processFullyLoadedChange(bool loading);
void updateRuthTimer(bool loading);
@@ -339,6 +363,28 @@ private:
LLFrameTimer mRuthTimer;
bool mFreezeTimeLangolier; // True when this avatar was created during snapshot FreezeTime mode, and that mode is still active.
bool mFreezeTimeDead; // True when the avatar was marked dead (ie, TP-ed away) while in FreezeTime mode.
public:
class ScopedPhaseSetter
{
public:
ScopedPhaseSetter(LLVOAvatar *avatarp, std::string phase_name):
mAvatar(avatarp), mPhaseName(phase_name)
{
if (mAvatar) { mAvatar->getPhases().startPhase(mPhaseName); }
}
~ScopedPhaseSetter()
{
if (mAvatar) { mAvatar->getPhases().stopPhase(mPhaseName); }
}
private:
std::string mPhaseName;
LLVOAvatar* mAvatar;
};
private:
LLViewerStats::PhaseMap mPhases;
protected:
LLFrameTimer mInvisibleTimer;
@@ -546,6 +592,7 @@ protected:
LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList ;
BOOL mLoadedCallbacksPaused;
std::set<LLUUID> mTextureIDs;
//--------------------------------------------------------------------
// Local Textures
//--------------------------------------------------------------------
@@ -644,6 +691,7 @@ protected:
**/
public:
void parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMessageContents& msg);
void processAvatarAppearance(LLMessageSystem* mesgsys);
void hideSkirt();
void startAppearanceAnimation();
@@ -662,7 +710,7 @@ public:
// True if this avatar should fetch its baked textures via the new
// appearance mechanism.
/*virtual*/ BOOL isUsingServerBakes() const { return mUseServerBakes; }
BOOL isUsingServerBakes() const;
void setIsUsingServerBakes(BOOL newval);

View File

@@ -61,6 +61,8 @@
#include "llappearancemgr.h"
#include "llmeshrepository.h"
#include "llvovolume.h"
#include "llsdutil.h"
#include "llsdserialize.h"
// [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a)
#include "rlvhandler.h"
#include "rlvlocks.h"
@@ -77,11 +79,36 @@ LLPointer<LLVOAvatarSelf> gAgentAvatarp = NULL;
BOOL isAgentAvatarValid()
{
return (gAgentAvatarp.notNull() &&
return (gAgentAvatarp.notNull() && gAgentAvatarp->isValid() &&
(gAgentAvatarp->getRegion() != NULL) &&
(!gAgentAvatarp->isDead()));
}
void selfStartPhase(const std::string& phase_name)
{
if (isAgentAvatarValid())
{
gAgentAvatarp->startPhase(phase_name);
}
}
void selfStopPhase(const std::string& phase_name)
{
if (isAgentAvatarValid())
{
gAgentAvatarp->stopPhase(phase_name);
}
}
void selfClearPhases()
{
if (isAgentAvatarValid())
{
gAgentAvatarp->clearPhases();
gAgentAvatarp->mLastRezzedStatus = -1;
}
}
using namespace LLAvatarAppearanceDefines;
/*********************************************************************************
@@ -151,6 +178,17 @@ LLVOAvatarSelf::LLVOAvatarSelf(const LLUUID& id,
lldebugs << "Marking avatar as self " << id << llendl;
}
// Called periodically for diagnostics, return true when done.
bool output_self_av_texture_diagnostics()
{
if (!isAgentAvatarValid())
return true; // done checking
gAgentAvatarp->outputRezDiagnostics();
return false;
}
void LLVOAvatarSelf::initInstance()
{
BOOL status = TRUE;
@@ -188,6 +226,9 @@ void LLVOAvatarSelf::initInstance()
llerrs << "Unable to load user's avatar" << llendl;
return;
}
//doPeriodically(output_self_av_texture_diagnostics, 30.0);
doPeriodically(boost::bind(&LLVOAvatarSelf::updateAvatarRezMetrics, this, false), 5.0);
}
// virtual
@@ -659,6 +700,15 @@ U32 LLVOAvatarSelf::processUpdateMessage(LLMessageSystem *mesgsys,
{
U32 retval = LLVOAvatar::processUpdateMessage(mesgsys,user_data,block_num,update_type,dp);
#if 0
// DRANO - it's not clear this does anything useful. If we wait
// until an appearance message has been received, we already have
// the texture ids. If we don't wait, we don't yet know where to
// look for baked textures, because we haven't received the
// appearance version data from the appearance message. This looks
// like an old optimization that's incompatible with server-side
// texture baking.
// FIXME DRANO - skipping in the case of !mFirstAppearanceMessageReceived prevents us from trying to
// load textures before we know where they come from (ie, from baking service or not);
// unknown impact on performance.
@@ -668,7 +718,7 @@ U32 LLVOAvatarSelf::processUpdateMessage(LLMessageSystem *mesgsys,
updateMeshTextures();
// unpack the texture UUIDs to the texture slots
retval = unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num);
retval = unpackTEMessage(mesgsys, _PREHASH_ObjectData, (S32) block_num);
// need to trigger a few operations to get the avatar to use the new bakes
for (U32 i = 0; i < mBakedTextureDatas.size(); i++)
@@ -683,6 +733,7 @@ U32 LLVOAvatarSelf::processUpdateMessage(LLMessageSystem *mesgsys,
mInitialBakesLoaded = true;
}
#endif
return retval;
}
@@ -1431,7 +1482,9 @@ BOOL LLVOAvatarSelf::isAllLocalTextureDataFinal() const
const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type);
for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++)
{
if (getLocalDiscardLevel(*local_tex_iter, wearable_index) > (S32)(desired_tex_discard_level))
S32 local_discard_level = getLocalDiscardLevel(*local_tex_iter, wearable_index);
if ((local_discard_level > (S32)(desired_tex_discard_level)) ||
(local_discard_level < 0 ))
{
return FALSE;
}
@@ -1994,6 +2047,84 @@ void LLVOAvatarSelf::debugBakedTextureUpload(EBakedTextureIndex index, BOOL fini
mDebugBakedTextureTimes[index][done] = mDebugSelfLoadTimer.getElapsedTimeF32();
}
const std::string LLVOAvatarSelf::verboseDebugDumpLocalTextureDataInfo(const LLViewerTexLayerSet* layerset) const
{
std::ostringstream outbuf;
for (LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter =
LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin();
baked_iter != LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end();
++baked_iter)
{
const EBakedTextureIndex baked_index = baked_iter->first;
if (layerset == mBakedTextureDatas[baked_index].mTexLayerSet)
{
outbuf << "baked_index: " << baked_index << "\n";
const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = baked_iter->second;
for (texture_vec_t::const_iterator local_tex_iter = baked_dict->mLocalTextures.begin();
local_tex_iter != baked_dict->mLocalTextures.end();
++local_tex_iter)
{
const ETextureIndex tex_index = *local_tex_iter;
const std::string tex_name = LLAvatarAppearanceDictionary::getInstance()->getTexture(tex_index)->mName;
outbuf << " tex_index " << (S32) tex_index << " name " << tex_name << "\n";
const LLWearableType::EType wearable_type = LLAvatarAppearanceDictionary::getTEWearableType(tex_index);
const U32 wearable_count = gAgentWearables.getWearableCount(wearable_type);
if (wearable_count > 0)
{
for (U32 wearable_index = 0; wearable_index < wearable_count; wearable_index++)
{
outbuf << " " << LLWearableType::getTypeName(wearable_type) << " " << wearable_index << ":";
const LLLocalTextureObject *local_tex_obj = getLocalTextureObject(tex_index, wearable_index);
if (local_tex_obj)
{
LLViewerFetchedTexture* image = dynamic_cast<LLViewerFetchedTexture*>( local_tex_obj->getImage() );
if (tex_index >= 0
&& local_tex_obj->getID() != IMG_DEFAULT_AVATAR
&& !image->isMissingAsset())
{
outbuf << " id: " << image->getID()
<< " refs: " << image->getNumRefs()
<< " glocdisc: " << getLocalDiscardLevel(tex_index, wearable_index)
<< " discard: " << image->getDiscardLevel()
<< " desired: " << image->getDesiredDiscardLevel()
<< " decode: " << image->getDecodePriority()
<< " addl: " << image->getAdditionalDecodePriority()
<< " ts: " << image->getTextureState()
<< " bl: " << image->getBoostLevel()
<< " fl: " << image->isFullyLoaded() // this is not an accessor for mFullyLoaded - see comment there.
<< " cl: " << (image->isFullyLoaded() && image->getDiscardLevel()==0) // "completely loaded"
<< " mvs: " << image->getMaxVirtualSize()
<< " mvsc: " << image->getMaxVirtualSizeResetCounter()
<< " mem: " << image->getTextureMemory();
}
}
outbuf << "\n";
}
}
}
break;
}
}
return outbuf.str();
}
void LLVOAvatarSelf::dumpAllTextures() const
{
std::string vd_text = "Local textures per baked index and wearable:\n";
for (LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::BakedTextures::const_iterator baked_iter = LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().begin();
baked_iter != LLAvatarAppearanceDefines::LLAvatarAppearanceDictionary::getInstance()->getBakedTextures().end();
++baked_iter)
{
const LLAvatarAppearanceDefines::EBakedTextureIndex baked_index = baked_iter->first;
const LLViewerTexLayerSet *layerset = debugGetLayerSet(baked_index);
if (!layerset) continue;
const LLViewerTexLayerSetBuffer *layerset_buffer = layerset->getViewerComposite();
if (!layerset_buffer) continue;
vd_text += verboseDebugDumpLocalTextureDataInfo(layerset);
}
LL_DEBUGS("Avatar") << vd_text << llendl;
}
const std::string LLVOAvatarSelf::debugDumpLocalTextureDataInfo(const LLViewerTexLayerSet* layerset) const
{
std::string text="";
@@ -2062,6 +2193,194 @@ const std::string LLVOAvatarSelf::debugDumpAllLocalTextureDataInfo() const
return text;
}
#if 0
// Dump avatar metrics data.
LLSD LLVOAvatarSelf::metricsData()
{
// runway - add region info
LLSD result;
result["rez_status"] = LLVOAvatar::rezStatusToString(getRezzedStatus());
result["timers"]["debug_existence"] = mDebugExistenceTimer.getElapsedTimeF32();
result["timers"]["ruth_debug"] = mRuthDebugTimer.getElapsedTimeF32();
result["timers"]["ruth"] = mRuthTimer.getElapsedTimeF32();
result["timers"]["invisible"] = mInvisibleTimer.getElapsedTimeF32();
result["timers"]["fully_loaded"] = mFullyLoadedTimer.getElapsedTimeF32();
result["startup"] = LLStartUp::getPhases().dumpPhases();
return result;
}
#endif
extern AIHTTPTimeoutPolicy appearanceChangeMetricsResponder_timeout;
class ViewerAppearanceChangeMetricsResponder: public LLHTTPClient::ResponderWithResult
{
public:
ViewerAppearanceChangeMetricsResponder( S32 expected_sequence,
volatile const S32 & live_sequence,
volatile bool & reporting_started):
mExpectedSequence(expected_sequence),
mLiveSequence(live_sequence),
mReportingStarted(reporting_started)
{
}
/*virtual*/ void result(LLSD const& content)
{
LL_DEBUGS("Avatar") << "OK" << LL_ENDL;
if (mLiveSequence == mExpectedSequence)
{
mReportingStarted = true;
}
}
/*virtual*/ void error(U32 status, std::string const& reason)
{
LL_WARNS("Avatar") << "Failed " << status << " reason " << reason << LL_ENDL;
}
/*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return appearanceChangeMetricsResponder_timeout; }
/*virtual*/ char const* getName(void) const { return "AppearanceChangeMetricsResponder"; }
private:
S32 mExpectedSequence;
volatile const S32 & mLiveSequence;
volatile bool & mReportingStarted;
};
bool LLVOAvatarSelf::updateAvatarRezMetrics(bool force_send)
{
const F32 AV_METRICS_INTERVAL_QA = 30.0;
F32 send_period = 300.0;
if (gSavedSettings.getBOOL("QAModeMetrics"))
{
send_period = AV_METRICS_INTERVAL_QA;
}
if (force_send || mTimeSinceLastRezMessage.getElapsedTimeF32() > send_period)
{
// Stats for completed phases have been getting logged as they
// complete. This will give us stats for any timers that
// haven't finished as of the metric's being sent.
LLVOAvatar::logPendingPhasesAllAvatars();
sendViewerAppearanceChangeMetrics();
}
return false;
}
void LLVOAvatarSelf::addMetricsTimerRecord(const LLSD& record)
{
mPendingTimerRecords.push_back(record);
}
bool operator<(const LLSD& a, const LLSD& b)
{
std::ostringstream aout, bout;
aout << LLSDNotationStreamer(a);
bout << LLSDNotationStreamer(b);
std::string astring = aout.str();
std::string bstring = bout.str();
return astring < bstring;
}
// Given a vector of LLSD records, return an LLSD array of bucketed stats for val_field.
LLSD summarize_by_buckets(std::vector<LLSD> in_records,
std::vector<std::string> by_fields,
std::string& val_field)
{
LLSD result = LLSD::emptyArray();
std::map<LLSD,LLViewerStats::StatsAccumulator> accum;
for (std::vector<LLSD>::iterator in_record_iter = in_records.begin();
in_record_iter != in_records.end(); ++in_record_iter)
{
LLSD& record = *in_record_iter;
LLSD key;
for (std::vector<std::string>::iterator field_iter = by_fields.begin();
field_iter != by_fields.end(); ++field_iter)
{
const std::string& field = *field_iter;
key[field] = record[field];
}
LLViewerStats::StatsAccumulator& stats = accum[key];
F32 value = record[val_field].asReal();
stats.push(value);
}
for (std::map<LLSD,LLViewerStats::StatsAccumulator>::iterator accum_it = accum.begin();
accum_it != accum.end(); ++accum_it)
{
LLSD out_record = accum_it->first;
out_record["stats"] = accum_it->second.getData();
result.append(out_record);
}
return result;
}
void LLVOAvatarSelf::sendViewerAppearanceChangeMetrics()
{
// gAgentAvatarp->stopAllPhases();
static volatile bool reporting_started(false);
static volatile S32 report_sequence(0);
LLSD msg; // = metricsData();
msg["message"] = "ViewerAppearanceChangeMetrics";
msg["session_id"] = gAgentSessionID;
msg["agent_id"] = gAgentID;
msg["sequence"] = report_sequence;
msg["initial"] = !reporting_started;
msg["break"] = false;
msg["duration"] = mTimeSinceLastRezMessage.getElapsedTimeF32();
// Status of our own rezzing.
msg["rez_status"] = LLVOAvatar::rezStatusToString(getRezzedStatus());
// Status of all nearby avs including ourself.
msg["nearby"] = LLSD::emptyArray();
std::vector<S32> rez_counts;
LLVOAvatar::getNearbyRezzedStats(rez_counts);
for (std::vector<S32>::size_type 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];
}
// std::vector<std::string> bucket_fields("timer_name","agent_id","is_self","grid_x","grid_y","is_using_server_bake");
std::vector<std::string> by_fields;
by_fields.push_back("timer_name");
by_fields.push_back("agent_id");
by_fields.push_back("completed");
by_fields.push_back("grid_x");
by_fields.push_back("grid_y");
by_fields.push_back("is_using_server_bakes");
by_fields.push_back("is_self");
by_fields.push_back("central_bake_version");
LLSD summary = summarize_by_buckets(mPendingTimerRecords, by_fields, std::string("elapsed"));
msg["timers"] = summary;
mPendingTimerRecords.clear();
// Update sequence number
if (S32_MAX == ++report_sequence)
report_sequence = 0;
LL_DEBUGS("Avatar") << avString() << "message: " << ll_pretty_print_sd(msg) << LL_ENDL;
std::string caps_url;
if (getRegion())
{
// runway - change here to activate.
caps_url = getRegion()->getCapability("ViewerMetrics");
}
if (!caps_url.empty())
{
AIHTTPHeaders headers;
LLHTTPClient::post(caps_url,
msg,
new ViewerAppearanceChangeMetricsResponder(report_sequence,
report_sequence,
reporting_started));
mTimeSinceLastRezMessage.reset();
}
}
const LLUUID& LLVOAvatarSelf::grabBakedTexture(EBakedTextureIndex baked_index) const
{
if (canGrabBakedTexture(baked_index))
@@ -2155,23 +2474,28 @@ void LLVOAvatarSelf::addLocalTextureStats( ETextureIndex type, LLViewerFetchedTe
{
if (!isIndexLocalTexture(type)) return;
if (getLocalTextureID(type, index) != IMG_DEFAULT_AVATAR && imagep->getDiscardLevel() != 0)
if (getLocalTextureID(type, index) != IMG_DEFAULT_AVATAR)
{
F32 desired_pixels;
desired_pixels = llmin(mPixelArea, (F32)getTexImageArea());
imagep->setNoDelete();
if (imagep->getDiscardLevel() != 0)
{
F32 desired_pixels;
desired_pixels = llmin(mPixelArea, (F32)getTexImageArea());
if (isUsingLocalAppearance())
{
imagep->setBoostLevel(getAvatarBoostLevel());
imagep->setAdditionalDecodePriority(SELF_ADDITIONAL_PRI) ;
}
imagep->resetTextureStats();
imagep->setMaxVirtualSizeResetInterval(MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL);
imagep->addTextureStats( desired_pixels / texel_area_ratio );
imagep->forceUpdateBindStats() ;
if (imagep->getDiscardLevel() < 0)
{
mHasGrey = TRUE; // for statistics gathering
// DRANO what priority should wearable-based textures have?
if (isUsingLocalAppearance())
{
imagep->setBoostLevel(getAvatarBoostLevel());
imagep->setAdditionalDecodePriority(SELF_ADDITIONAL_PRI) ;
}
imagep->resetTextureStats();
imagep->setMaxVirtualSizeResetInterval(MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL);
imagep->addTextureStats( desired_pixels / texel_area_ratio );
imagep->forceUpdateBindStats() ;
if (imagep->getDiscardLevel() < 0)
{
mHasGrey = TRUE; // for statistics gathering
}
}
}
else
@@ -2344,6 +2668,8 @@ void LLVOAvatarSelf::outputRezDiagnostics() const
if (!layerset_buffer) continue;
LL_DEBUGS("Avatar") << layerset_buffer->dumpTextureInfo() << llendl;
}
dumpAllTextures();
}
void LLVOAvatarSelf::outputRezTiming(const std::string& msg) const

View File

@@ -378,6 +378,13 @@ public:
LLUUID mAvatarID;
LLAvatarAppearanceDefines::ETextureIndex mIndex;
};
LLTimer mTimeSinceLastRezMessage;
bool updateAvatarRezMetrics(bool force_send);
std::vector<LLSD> mPendingTimerRecords;
void addMetricsTimerRecord(const LLSD& record);
void debugWearablesLoaded() { mDebugTimeWearablesLoaded = mDebugSelfLoadTimer.getElapsedTimeF32(); }
void debugAvatarVisible() { mDebugTimeAvatarVisible = mDebugSelfLoadTimer.getElapsedTimeF32(); }
void outputRezDiagnostics() const;
@@ -389,8 +396,11 @@ public:
BOOL isAllLocalTextureDataFinal() const;
const LLViewerTexLayerSet* debugGetLayerSet(LLAvatarAppearanceDefines::EBakedTextureIndex index) const { return (LLViewerTexLayerSet*)(mBakedTextureDatas[index].mTexLayerSet); }
const std::string verboseDebugDumpLocalTextureDataInfo(const LLViewerTexLayerSet* layerset) const; // Lists out state of this particular baked texture layer
void dumpAllTextures() const;
const std::string debugDumpLocalTextureDataInfo(const LLViewerTexLayerSet* layerset) const; // Lists out state of this particular baked texture layer
const std::string debugDumpAllLocalTextureDataInfo() const; // Lists out which baked textures are at highest LOD
void sendViewerAppearanceChangeMetrics(); // send data associated with completing a change.
private:
LLFrameTimer mDebugSelfLoadTimer;
F32 mDebugTimeWearablesLoaded;
@@ -412,4 +422,10 @@ extern LLPointer<LLVOAvatarSelf> gAgentAvatarp;
BOOL isAgentAvatarValid();
void selfStartPhase(const std::string& phase_name);
void selfStopPhase(const std::string& phase_name);
void selfClearPhases();
void update_avatar_rez_metrics(bool force_send);
#endif // LL_VO_AVATARSELF_H

View File

@@ -3260,10 +3260,6 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep,
else
{
model_mat = &(drawable->getRegion()->mRenderMatrix);
if (model_mat->isIdentity())
{
model_mat = NULL;
}
}
//drawable->getVObj()->setDebugText(llformat("%d", drawable->isState(LLDrawable::ANIMATED_CHILD)));

View File

@@ -676,7 +676,10 @@ void LLWorld::updateRegions(F32 max_update_time)
if (did_one && max_time <= 0.f)
break;
max_time = llmin(max_time, max_update_time*.1f);
did_one |= regionp->idleUpdate(max_update_time);
if (regionp->idleUpdate(max_update_time))
{
did_one = TRUE;
}
}
}
@@ -1450,7 +1453,8 @@ void LLWorld::getAvatars(std::vector<LLUUID>* avatar_ids, std::vector<LLVector3d
iter != LLCharacter::sInstances.end(); ++iter)
{
LLVOAvatar* pVOAvatar = (LLVOAvatar*) *iter;
if(!pVOAvatar->isDead() && !pVOAvatar->isSelf())
if (!pVOAvatar->isDead() && !pVOAvatar->isSelf() && !pVOAvatar->mIsDummy)
{
LLUUID uuid = pVOAvatar->getID();
if(!uuid.isNull())