From 8cc601c3e26d57800fd15795c542eae5e6348f78 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Thu, 14 Feb 2013 15:48:00 -0500 Subject: [PATCH 01/26] Fix ups for the Permissions Panel Minor update and cleanup to llpanelpermissions.cpp Hide the You can modify this object text properly when Debugging Permissions Show Debug Permissions on the same line on the build panel. --- indra/newview/llpanelpermissions.cpp | 118 +++++++++--------- .../skins/default/xui/en-us/floater_tools.xml | 20 ++- 2 files changed, 65 insertions(+), 73 deletions(-) diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index ecc652645..0c5fde59c 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -89,42 +89,42 @@ LLPanelPermissions::LLPanelPermissions(const std::string& title) : BOOL LLPanelPermissions::postBuild() { - this->childSetCommitCallback("Object Name",LLPanelPermissions::onCommitName,this); - this->childSetPrevalidate("Object Name",LLLineEditor::prevalidatePrintableNotPipe); - this->childSetCommitCallback("Object Description",LLPanelPermissions::onCommitDesc,this); - this->childSetPrevalidate("Object Description",LLLineEditor::prevalidatePrintableNotPipe); + childSetCommitCallback("Object Name",LLPanelPermissions::onCommitName,this); + childSetPrevalidate("Object Name",LLLineEditor::prevalidatePrintableNotPipe); + childSetCommitCallback("Object Description",LLPanelPermissions::onCommitDesc,this); + childSetPrevalidate("Object Description",LLLineEditor::prevalidatePrintableNotPipe); - this->childSetAction("button owner profile",LLPanelPermissions::onClickOwner,this); - this->childSetAction("button last owner profile",LLPanelPermissions::onClickLastOwner,this); - this->childSetAction("button creator profile",LLPanelPermissions::onClickCreator,this); + childSetAction("button owner profile",LLPanelPermissions::onClickOwner,this); + childSetAction("button last owner profile",LLPanelPermissions::onClickLastOwner,this); + childSetAction("button creator profile",LLPanelPermissions::onClickCreator,this); - this->childSetAction("button set group",LLPanelPermissions::onClickGroup,this); - this->childSetAction("button open group",LLPanelPermissions::onClickOpenGroup,this); + childSetAction("button set group",LLPanelPermissions::onClickGroup,this); + childSetAction("button open group",LLPanelPermissions::onClickOpenGroup,this); - this->childSetCommitCallback("checkbox share with group",LLPanelPermissions::onCommitGroupShare,this); + childSetCommitCallback("checkbox share with group",LLPanelPermissions::onCommitGroupShare,this); - this->childSetAction("button deed",LLPanelPermissions::onClickDeedToGroup,this); + childSetAction("button deed",LLPanelPermissions::onClickDeedToGroup,this); - this->childSetAction("button cpy_key",LLPanelPermissions::onClickCopyObjKey,this); + childSetAction("button cpy_key",LLPanelPermissions::onClickCopyObjKey,this); - this->childSetCommitCallback("checkbox allow everyone move",LLPanelPermissions::onCommitEveryoneMove,this); + childSetCommitCallback("checkbox allow everyone move",LLPanelPermissions::onCommitEveryoneMove,this); - this->childSetCommitCallback("checkbox allow everyone copy",LLPanelPermissions::onCommitEveryoneCopy,this); - - this->childSetCommitCallback("checkbox for sale",LLPanelPermissions::onCommitSaleInfo,this); + childSetCommitCallback("checkbox allow everyone copy",LLPanelPermissions::onCommitEveryoneCopy,this); - this->childSetCommitCallback("Edit Cost",LLPanelPermissions::onCommitSaleInfo,this); - this->childSetPrevalidate("Edit Cost",LLLineEditor::prevalidateNonNegativeS32); + childSetCommitCallback("checkbox for sale",LLPanelPermissions::onCommitSaleInfo,this); + + childSetCommitCallback("Edit Cost",LLPanelPermissions::onCommitSaleInfo,this); + childSetPrevalidate("Edit Cost",LLLineEditor::prevalidateNonNegativeS32); + + childSetCommitCallback("sale type",LLPanelPermissions::onCommitSaleType,this); + + childSetCommitCallback("checkbox next owner can modify",LLPanelPermissions::onCommitNextOwnerModify,this); + childSetCommitCallback("checkbox next owner can copy",LLPanelPermissions::onCommitNextOwnerCopy,this); + childSetCommitCallback("checkbox next owner can transfer",LLPanelPermissions::onCommitNextOwnerTransfer,this); + childSetCommitCallback("clickaction",LLPanelPermissions::onCommitClickAction,this); + childSetCommitCallback("search_check",LLPanelPermissions::onCommitIncludeInSearch,this); - this->childSetCommitCallback("sale type",LLPanelPermissions::onCommitSaleType,this); - - this->childSetCommitCallback("checkbox next owner can modify",LLPanelPermissions::onCommitNextOwnerModify,this); - this->childSetCommitCallback("checkbox next owner can copy",LLPanelPermissions::onCommitNextOwnerCopy,this); - this->childSetCommitCallback("checkbox next owner can transfer",LLPanelPermissions::onCommitNextOwnerTransfer,this); - this->childSetCommitCallback("clickaction",LLPanelPermissions::onCommitClickAction,this); - this->childSetCommitCallback("search_check",LLPanelPermissions::onCommitIncludeInSearch,this); - LLTextBox* group_rect_proxy = getChild("Group Name Proxy"); if(group_rect_proxy ) { @@ -284,8 +284,8 @@ void LLPanelPermissions::refresh() BOOL is_nonpermanent_enforced = (LLSelectMgr::getInstance()->getSelection()->getFirstRootNode() && LLSelectMgr::getInstance()->selectGetRootsNonPermanentEnforced()) || LLSelectMgr::getInstance()->selectGetNonPermanentEnforced(); - const LLFocusableElement* keyboard_focus_view = gFocusMgr.getKeyboardFocus(); + S32 string_index = 0; std::string MODIFY_INFO_STRINGS[] = { @@ -569,51 +569,58 @@ void LLPanelPermissions::refresh() // based on who owns the object. // TODO: Creator permissions - BOOL valid_base_perms = FALSE; - BOOL valid_owner_perms = FALSE; - BOOL valid_group_perms = FALSE; - BOOL valid_everyone_perms = FALSE; - BOOL valid_next_perms = FALSE; - - U32 base_mask_on; - U32 base_mask_off; - U32 owner_mask_on; - U32 owner_mask_off; - U32 group_mask_on; - U32 group_mask_off; - U32 everyone_mask_on; - U32 everyone_mask_off; + U32 base_mask_on = 0; + U32 base_mask_off = 0; + U32 owner_mask_off = 0; + U32 owner_mask_on = 0; + U32 group_mask_on = 0; + U32 group_mask_off = 0; + U32 everyone_mask_on = 0; + U32 everyone_mask_off = 0; U32 next_owner_mask_on = 0; U32 next_owner_mask_off = 0; - valid_base_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_BASE, + BOOL valid_base_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_BASE, &base_mask_on, &base_mask_off); - valid_owner_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_OWNER, + //BOOL valid_owner_perms =// + LLSelectMgr::getInstance()->selectGetPerm(PERM_OWNER, &owner_mask_on, &owner_mask_off); - valid_group_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_GROUP, + BOOL valid_group_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_GROUP, &group_mask_on, &group_mask_off); - valid_everyone_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_EVERYONE, + BOOL valid_everyone_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_EVERYONE, &everyone_mask_on, &everyone_mask_off); - valid_next_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_NEXT_OWNER, + BOOL valid_next_perms = LLSelectMgr::getInstance()->selectGetPerm(PERM_NEXT_OWNER, &next_owner_mask_on, &next_owner_mask_off); if( gSavedSettings.getBOOL("DebugPermissions") ) { + childSetVisible("perm_modify", false); std::string perm_string; if (valid_base_perms) { perm_string = "B: "; perm_string += mask_to_string(base_mask_on); + if (U32 diff_mask = base_mask_on ^ owner_mask_on) // When different, show the user's potential permissions lowercase. + { + if (diff_mask & PERM_MOVE) + LLStringUtil::replaceChar(perm_string, 'V', 'v'); + if (diff_mask & PERM_MODIFY) + LLStringUtil::replaceChar(perm_string, 'M', 'm'); + if (diff_mask & PERM_COPY) + LLStringUtil::replaceChar(perm_string, 'C', 'c'); + if (diff_mask & PERM_TRANSFER) + LLStringUtil::replaceChar(perm_string, 'T', 't'); + } childSetText("B:",perm_string); childSetVisible("B:",true); @@ -639,20 +646,17 @@ void LLPanelPermissions::refresh() } perm_string = "F: "; U32 flag_mask = 0x0; - if (objectp->permMove()) - flag_mask |= PERM_MOVE; - if (objectp->permModify()) - flag_mask |= PERM_MODIFY; - if (objectp->permCopy()) - flag_mask |= PERM_COPY; - if (objectp->permTransfer()) - flag_mask |= PERM_TRANSFER; + if (objectp->permMove()) flag_mask |= PERM_MOVE; + if (objectp->permModify()) flag_mask |= PERM_MODIFY; + if (objectp->permCopy()) flag_mask |= PERM_COPY; + if (objectp->permTransfer()) flag_mask |= PERM_TRANSFER; perm_string += mask_to_string(flag_mask); childSetText("F:",perm_string); childSetVisible("F:",true); } else { + childSetVisible("perm_modify", true); childSetVisible("B:",false); childSetVisible("O:",false); childSetVisible("G:",false); @@ -931,12 +935,6 @@ void LLPanelPermissions::onClickOwner(void *data) } } - - - - - - void LLPanelPermissions::onClickLastOwner(void *data) { LLPanelPermissions *self = (LLPanelPermissions *)data; diff --git a/indra/newview/skins/default/xui/en-us/floater_tools.xml b/indra/newview/skins/default/xui/en-us/floater_tools.xml index fe18acba2..cef3bf20e 100644 --- a/indra/newview/skins/default/xui/en-us/floater_tools.xml +++ b/indra/newview/skins/default/xui/en-us/floater_tools.xml @@ -518,37 +518,31 @@ tool_tip="Copies the object key to the clipboard." width="78" /> B: - O; - - G: E: N: F: From cd6bda876d9c624e4f9555e9a8ede8b4fb94c9b0 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Fri, 15 Feb 2013 08:28:54 -0500 Subject: [PATCH 02/26] Add RememberName setting to settings.xml (No wonder it wasn't saving!) --- indra/newview/app_settings/settings.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index b52c70e5d..596081313 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10849,6 +10849,17 @@ This should be as low as possible, but too low may break functionality Value 256 + RememberName + + Comment + Add name to list of saved names offered on login + Persist + 1 + Type + Boolean + Value + 1 + RememberPassword Comment From 036328c3ac49f298ea6367651b42cceecfe61b5f Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 17 Feb 2013 19:41:59 -0500 Subject: [PATCH 03/26] Allow finding objects by typing on the script info scrolllists Also tweaks columns widths and sort order to present information more effectively. --- .../en-us/panel_script_limits_my_avatar.xml | 19 +++++++-------- .../panel_script_limits_region_memory.xml | 23 ++++++++++--------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/indra/newview/skins/default/xui/en-us/panel_script_limits_my_avatar.xml b/indra/newview/skins/default/xui/en-us/panel_script_limits_my_avatar.xml index f1ea6756e..5d1af7f3a 100644 --- a/indra/newview/skins/default/xui/en-us/panel_script_limits_my_avatar.xml +++ b/indra/newview/skins/default/xui/en-us/panel_script_limits_my_avatar.xml @@ -73,23 +73,24 @@ layout="topleft" left_delta="0" multi_select="true" - sort_column="0" - sort_ascending="true" + sort_column="1" + sort_ascending="false" name="scripts_list" top_delta="16" width="460"> + + width="40" /> - + width="45" /> + + width="40" /> - + width="45" /> + width="130" /> + width="60" /> Date: Sun, 17 Feb 2013 21:00:51 -0500 Subject: [PATCH 04/26] Fix up merge of llviewermessage.cpp between Shyotl/sunshine and Canon. --- indra/newview/llviewermessage.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index d9dcf4384..c66a706d8 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -5814,30 +5814,35 @@ static void money_balance_group_notify(const LLUUID& group_id, const std::string& name, bool is_group, std::string message, - std::string notification, LLStringUtil::format_map_t args, LLSD payload) { + bool no_transaction_clutter = gSavedSettings.getBOOL("LiruNoTransactionClutter"); + std::string notification = no_transaction_clutter ? "Payment" : "SystemMessage"; args["NAME"] = name; LLSD msg_args; msg_args["MESSAGE"] = LLTrans::getString(message,args); LLNotificationsUtil::add(notification,msg_args,payload); + + if (!no_transaction_clutter) LLFloaterChat::addChat(msg_args["MESSAGE"].asString()); // Alerts won't automatically log to chat. } static void money_balance_avatar_notify(const LLUUID& agent_id, const LLAvatarName& av_name, std::string message, - std::string notification, LLStringUtil::format_map_t args, LLSD payload) { - + bool no_transaction_clutter = gSavedSettings.getBOOL("LiruNoTransactionClutter"); + std::string notification = no_transaction_clutter ? "Payment" : "SystemMessage"; std::string name; LLAvatarNameCache::getPNSName(av_name,name); args["NAME"] = name; LLSD msg_args; msg_args["MESSAGE"] = LLTrans::getString(message,args); LLNotificationsUtil::add(notification,msg_args,payload); + + if (!no_transaction_clutter) LLFloaterChat::addChat(msg_args["MESSAGE"].asString()); // Alerts won't automatically log to chat. } static void process_money_balance_reply_extended(LLMessageSystem* msg) { @@ -5883,8 +5888,6 @@ static void process_money_balance_reply_extended(LLMessageSystem* msg) bool is_name_group = false; LLUUID name_id; std::string message; - static LLCachedControl no_transaction_clutter("LiruNoTransactionClutter", false); - std::string notification = no_transaction_clutter ? "Payment" : "SystemMessage"; LLSD payload; bool you_paid_someone = (source_id == gAgentID); @@ -5947,16 +5950,14 @@ static void process_money_balance_reply_extended(LLMessageSystem* msg) gCacheName->getGroup(name_id, boost::bind(&money_balance_group_notify, _1, _2, _3, message, - notification, args, payload)); + args, payload)); } else { LLAvatarNameCache::get(name_id, boost::bind(&money_balance_avatar_notify, _1, _2, message, - notification, args, payload)); + args, payload)); } - - if (!no_transaction_clutter) LLFloaterChat::addChat(message); // Alerts won't automatically log to chat. } bool handle_prompt_for_maturity_level_change_callback(const LLSD& notification, const LLSD& response) From e01dd3292fe7298b03da27cd96dbf217165a27d2 Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Sun, 17 Feb 2013 23:19:23 -0500 Subject: [PATCH 05/26] Add Script Info menu entry to the self pie menu... From the comments in llfloaterscriptlimits.cpp, it seems like this was intended to be a feature. --- indra/newview/llviewermenu.cpp | 16 ++++++++++++++++ .../skins/default/xui/en-us/menu_pie_self.xml | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 7cd5efada..b2f8bade8 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -149,6 +149,7 @@ #include "llfloaterregioninfo.h" #include "llfloaterreporter.h" #include "llfloaterscriptdebug.h" +#include "llfloaterscriptlimits.h" #include "llfloatersettingsdebug.h" #include "llfloaterenvsettings.h" @@ -2553,6 +2554,16 @@ class LLSelfEnableRemoveAllAttachments : public view_listener_t } }; +class LLSelfVisibleScriptInfo : public view_listener_t +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + if (LLViewerRegion* region = gAgent.getRegion()) + gMenuHolder->findControl(userdata["control"].asString())->setValue(!region->getCapability("AttachmentResources").empty()); + return true; + } +}; + BOOL enable_has_attachments(void*) { @@ -6671,6 +6682,10 @@ class LLShowFloater : public view_listener_t { LLFloaterScriptDebug::show(LLUUID::null); } + else if (floater_name == "script info") + { + LLFloaterScriptLimits::showInstance(); + } else if (floater_name == "help f1") { llinfos << "Spawning HTML help window" << llendl; @@ -9606,6 +9621,7 @@ void initialize_menus() addMenu(new LLSelfEnableSitOrStand(), "Self.EnableSitOrStand"); addMenu(new LLSelfEnableRemoveAllAttachments(), "Self.EnableRemoveAllAttachments"); + addMenu(new LLSelfVisibleScriptInfo(), "Self.VisibleScriptInfo"); // Avatar pie menu diff --git a/indra/newview/skins/default/xui/en-us/menu_pie_self.xml b/indra/newview/skins/default/xui/en-us/menu_pie_self.xml index 07e2c301f..335f3e4c0 100644 --- a/indra/newview/skins/default/xui/en-us/menu_pie_self.xml +++ b/indra/newview/skins/default/xui/en-us/menu_pie_self.xml @@ -97,6 +97,10 @@ + + + + From 4c33328906981f76784b472a36b1007ad8eb264c Mon Sep 17 00:00:00 2001 From: Lirusaito Date: Mon, 18 Feb 2013 17:57:40 -0500 Subject: [PATCH 06/26] If RLVa is off, RLVa z offset should not be enforced. Easier diff view without space changes. --- indra/newview/llagent.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 2539c8f91..6d6c6be58 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -4460,9 +4460,14 @@ void LLAgent::sendAgentSetAppearance() body_size.mV[VX] += x_off; body_size.mV[VY] += y_off; // [RLVa:KB] - Checked: 2010-10-11 (RLVa-1.2.0e) | Added: RLVa-1.2.0e - F32 rlvz_off = RlvSettings::getAvatarOffsetZ(); - body_size.mV[VZ] += fabs(rlvz_off) ? rlvz_off : z_off; + if (rlv_handler_t::isEnabled()) + { + F32 rlvz_off = RlvSettings::getAvatarOffsetZ(); + body_size.mV[VZ] += fabs(rlvz_off) ? rlvz_off : z_off; + } + else // [/RLVa:KB] + body_size.mV[VZ] += z_off; msg->addVector3Fast(_PREHASH_Size, body_size); From 2b9c0e34c7ff535d7624561d23c8d724691217fc Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 19 Feb 2013 03:27:56 +0100 Subject: [PATCH 07/26] Fix the Octopus to not segfault anymore --- indra/newview/hippofloaterxml.cpp | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/indra/newview/hippofloaterxml.cpp b/indra/newview/hippofloaterxml.cpp index c49788692..bdc1acd6e 100644 --- a/indra/newview/hippofloaterxml.cpp +++ b/indra/newview/hippofloaterxml.cpp @@ -265,8 +265,9 @@ void HippoFloaterXml::execute(const std::string &cmds) // ******************************************************************** // generic notification callbacks -static void notifyCallback(LLUICtrl *ctrl) +static void notifyCallback(void *c) { + LLUICtrl *ctrl = (LLUICtrl *)c; std::string msg = "NOTIFY:"; msg += ctrl->getName(); msg += ':'; @@ -324,20 +325,17 @@ bool HippoFloaterXmlImpl::execute(LLUICtrl *ctrl, bool set = (value != "0"); if (HippoFloaterXmlImpl *floater = dynamic_cast(ctrl)) { floater->mIsNotifyOnClose = set; - } else if (LLButton *button = dynamic_cast(ctrl)) - { - if (set) - floater->mNotices[button] = notice_ptr_t(new notice_connection_t(button->setClickedCallback(boost::bind(¬ifyCallback, ctrl)))); - else - floater->mNotices.erase(button); - } - else - { - if (set) - floater->mNotices[ctrl] = notice_ptr_t(new notice_connection_t(ctrl->setCommitCallback(boost::bind(¬ifyCallback, ctrl)))); - else - floater->mNotices.erase(ctrl); - } + } else if (LLButton *button = dynamic_cast(ctrl)) { + if (set) + button->setClickedCallback(boost::bind(¬ifyCallback, _1), ctrl); + else + button->setClickedCallback(0, 0); + } else { + if (set) + ctrl->setCommitCallback(boost::bind(¬ifyCallback, _1), ctrl); + else + ctrl->setCommitCallback(0); + } } } } From 994dfc5b299843d527792d403b9986c237c3da92 Mon Sep 17 00:00:00 2001 From: Melanie Date: Tue, 19 Feb 2013 04:41:04 +0100 Subject: [PATCH 08/26] Simplify the previous commit because commit callback for buttons is now identical to the clicked callback, obviating the need to distinguish between these control types. --- indra/newview/hippofloaterxml.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/indra/newview/hippofloaterxml.cpp b/indra/newview/hippofloaterxml.cpp index bdc1acd6e..d8af038a6 100644 --- a/indra/newview/hippofloaterxml.cpp +++ b/indra/newview/hippofloaterxml.cpp @@ -325,11 +325,6 @@ bool HippoFloaterXmlImpl::execute(LLUICtrl *ctrl, bool set = (value != "0"); if (HippoFloaterXmlImpl *floater = dynamic_cast(ctrl)) { floater->mIsNotifyOnClose = set; - } else if (LLButton *button = dynamic_cast(ctrl)) { - if (set) - button->setClickedCallback(boost::bind(¬ifyCallback, _1), ctrl); - else - button->setClickedCallback(0, 0); } else { if (set) ctrl->setCommitCallback(boost::bind(¬ifyCallback, _1), ctrl); From 76f3dc9e19c7a0a96fba641caec2a5b1dec086d0 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 19 Feb 2013 01:50:46 -0600 Subject: [PATCH 09/26] Massive sunshine/viewer-dev catchup. Further implemented serverside baking and added/updated metrics feedback. --- indra/llappearance/lltexlayer.cpp | 90 ++- indra/llappearance/lltexlayer.h | 2 +- indra/llappearance/lltexlayerparams.cpp | 4 - indra/llappearance/llwearabledata.cpp | 21 +- indra/llappearance/llwearabledata.h | 8 +- indra/llcharacter/llhandmotion.cpp | 67 +- indra/llcharacter/llmotioncontroller.cpp | 3 +- indra/llcharacter/llmotioncontroller.h | 6 +- indra/llcommon/lllslconstants.h | 181 +---- indra/llimage/llimage.cpp | 23 + indra/llimage/llimage.h | 5 + indra/llinventory/llinventory.cpp | 44 +- indra/llinventory/llinventory.h | 10 +- indra/llinventory/llparcel.cpp | 2 +- indra/llinventory/llparcel.h | 1 + indra/llmath/lloctree.h | 2 +- indra/llmath/llvolume.cpp | 236 ------ indra/llmath/llvolume.h | 13 +- indra/llmessage/aihttptimeoutpolicy.cpp | 2 + indra/llmessage/llxfermanager.cpp | 13 +- indra/llrender/llgltexture.h | 21 +- indra/newview/CMakeLists.txt | 3 + .../shaders/class1/avatar/objectSkinV.glsl | 28 +- indra/newview/character/avatar_lad.xml | 11 + indra/newview/llagent.cpp | 101 ++- indra/newview/llagent.h | 3 +- indra/newview/llagentcamera.cpp | 6 +- indra/newview/llagentwearables.cpp | 16 +- indra/newview/llagentwearables.h | 6 +- indra/newview/llagentwearablesfetch.cpp | 2 + indra/newview/llappearancemgr.cpp | 292 ++++--- indra/newview/llappearancemgr.h | 13 +- indra/newview/llappviewer.cpp | 134 +++- indra/newview/llappviewer.h | 4 + indra/newview/lldrawable.cpp | 24 +- indra/newview/lldrawpoolavatar.cpp | 5 + indra/newview/lldrawpoolbump.cpp | 2 +- indra/newview/llflexibleobject.cpp | 4 +- indra/newview/llfloatercolorpicker.cpp | 3 +- indra/newview/llfloatercustomize.cpp | 98 ++- indra/newview/llfloatercustomize.h | 1 + indra/newview/llfloaterland.cpp | 2 +- indra/newview/llinventorybridge.cpp | 7 +- indra/newview/llmanip.cpp | 9 +- indra/newview/llmorphview.cpp | 4 +- indra/newview/llpaneleditwearable.cpp | 39 +- indra/newview/llpanelpermissions.cpp | 2 +- indra/newview/llsimplestat.h | 152 ++++ indra/newview/llstartup.cpp | 31 +- indra/newview/llstartup.h | 13 +- indra/newview/lltexturefetch.cpp | 586 +++++++++++++- indra/newview/lltexturefetch.h | 48 +- indra/newview/lltoolfocus.cpp | 2 + indra/newview/llviewerassetstats.cpp | 611 +++++++++++++++ indra/newview/llviewerassetstats.h | 327 ++++++++ indra/newview/llviewerassetstorage.cpp | 178 ++++- indra/newview/llviewerassetstorage.h | 50 +- indra/newview/llviewerobject.cpp | 29 +- indra/newview/llviewerobject.h | 3 +- indra/newview/llviewerparcelmgr.cpp | 11 + indra/newview/llviewerregion.cpp | 4 +- indra/newview/llviewerstats.cpp | 69 ++ indra/newview/llviewerstats.h | 20 + indra/newview/llviewertexture.cpp | 2 +- indra/newview/llviewertexture.h | 2 + indra/newview/llviewertexturelist.cpp | 47 +- indra/newview/llvoavatar.cpp | 739 ++++++++++++++---- indra/newview/llvoavatar.h | 52 +- indra/newview/llvoavatarself.cpp | 362 ++++++++- indra/newview/llvoavatarself.h | 16 + indra/newview/llvovolume.cpp | 4 - indra/newview/llworld.cpp | 8 +- 72 files changed, 3945 insertions(+), 994 deletions(-) create mode 100644 indra/newview/llsimplestat.h create mode 100644 indra/newview/llviewerassetstats.cpp create mode 100644 indra/newview/llviewerassetstats.h diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp index d1edd4f09..3c23f5f29 100644 --- a/indra/llappearance/lltexlayer.cpp +++ b/indra/llappearance/lltexlayer.cpp @@ -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 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); diff --git a/indra/llappearance/lltexlayer.h b/indra/llappearance/lltexlayer.h index f267753f2..712e92c00 100644 --- a/indra/llappearance/lltexlayer.h +++ b/indra/llappearance/lltexlayer.h @@ -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; diff --git a/indra/llappearance/lltexlayerparams.cpp b/indra/llappearance/lltexlayerparams.cpp index 0cdeb48ce..64b3b6218 100644 --- a/indra/llappearance/lltexlayerparams.cpp +++ b/indra/llappearance/lltexlayerparams.cpp @@ -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(); } diff --git a/indra/llappearance/llwearabledata.cpp b/indra/llappearance/llwearabledata.cpp index f3ddfcb2f..4d06c56f7 100644 --- a/indra/llappearance/llwearabledata.cpp +++ b/indra/llappearance/llwearabledata.cpp @@ -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); diff --git a/indra/llappearance/llwearabledata.h b/indra/llappearance/llwearabledata.h index c3f80a72d..7c90dbde8 100644 --- a/indra/llappearance/llwearabledata.h +++ b/indra/llappearance/llwearabledata.h @@ -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 diff --git a/indra/llcharacter/llhandmotion.cpp b/indra/llcharacter/llhandmotion.cpp index d992b349e..696dba0d9 100644 --- a/indra/llcharacter/llhandmotion.cpp +++ b/indra/llcharacter/llhandmotion.cpp @@ -127,30 +127,73 @@ BOOL LLHandMotion::onUpdate(F32 time, U8* joint_mask) mLastTime = time; requestedHandPose = (eHandPose *)mCharacter->getAnimationData("Hand Pose"); - // - if(requestedHandPose && *requestedHandPose >= NUM_HAND_POSES) - { - llwarns << "requested hand pose >= NUM_HAND_POSES" << llendl; - requestedHandPose = &mCurrentPose; - } - // // 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"); diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index eecbd1823..c7cf2655c 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -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), diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h index f8bf5ac28..a0a940a9e 100644 --- a/indra/llcharacter/llmotioncontroller.h +++ b/indra/llcharacter/llmotioncontroller.h @@ -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; diff --git a/indra/llcommon/lllslconstants.h b/indra/llcommon/lllslconstants.h index 9452d7031..9f32598e6 100644 --- a/indra/llcommon/lllslconstants.h +++ b/indra/llcommon/lllslconstants.h @@ -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,17 +179,9 @@ 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. -char const* const TEXTBOX_MAGIC_TOKEN = "!!llTextBox!!"; +// llTextBox() magic token string - yes this is a hack. sue me. +char const* const TEXTBOX_MAGIC_TOKEN = "!!llTextBox!!"; // changed() event flags const U32 CHANGED_NONE = 0x0; @@ -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 diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index aa0086a53..1f7b47298 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -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 ) { diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index a9b7485da..6d6844e0e 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -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 ); diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 31cca09fd..7a1baada0 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -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& 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 item) return rv; } -LLPointer ll_create_item_from_sd(const LLSD& sd_item) -{ - LLPointer 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 cat) { LLSD rv; diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index bf701d39a..fd362a6a2 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -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& 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 item); -LLPointer ll_create_item_from_sd(const LLSD& sd_item); LLSD ll_create_sd_from_inventory_category(LLPointer cat); LLPointer ll_create_category_from_sd(const LLSD& sd_cat); diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index 8512a248b..99a37acd5 100644 --- a/indra/llinventory/llparcel.cpp +++ b/indra/llinventory/llparcel.cpp @@ -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))); diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h index e8bb3f182..ce3e0eab1 100644 --- a/indra/llinventory/llparcel.h +++ b/indra/llinventory/llparcel.h @@ -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 diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h index d22f2d70d..1d2b7c257 100644 --- a/indra/llmath/lloctree.h +++ b/indra/llmath/lloctree.h @@ -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; diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 24bd48bed..1f7862ea8 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -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& 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 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 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]; diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 16b640fe7..f6e974774 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -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 &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); @@ -1045,7 +1034,7 @@ public: friend std::ostream& operator<<(std::ostream &s, const LLVolume *volumep); // HACK to bypass Windoze confusion over // conversion if *(LLVolume*) to LLVolume& const LLVolumeFace &getVolumeFace(const S32 f) const {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE - + U32 mFaceMask; // bit array of which faces exist in this volume LLVector3 mLODScaleBias; // vector for biasing LOD based on scale diff --git a/indra/llmessage/aihttptimeoutpolicy.cpp b/indra/llmessage/aihttptimeoutpolicy.cpp index a45b990e9..da2a38250 100644 --- a/indra/llmessage/aihttptimeoutpolicy.cpp +++ b/indra/llmessage/aihttptimeoutpolicy.cpp @@ -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); diff --git a/indra/llmessage/llxfermanager.cpp b/indra/llmessage/llxfermanager.cpp index b9cddc8e4..00b9d8161 100644 --- a/indra/llmessage/llxfermanager.cpp +++ b/indra/llmessage/llxfermanager.cpp @@ -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; diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h index 6516dd8bc..8d2fc180f 100644 --- a/indra/llrender/llgltexture.h +++ b/indra/llrender/llgltexture.h @@ -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 //--------------------------------------------------------------------------------------------- @@ -178,15 +189,7 @@ protected: LLPointer mGLTexturep ; 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; +protected: LLGLTextureState mTextureState ; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c068de93a..aa67b41e4 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -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 diff --git a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl index 43ed41a20..03d0c82c2 100644 --- a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl +++ b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl @@ -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; } diff --git a/indra/newview/character/avatar_lad.xml b/indra/newview/character/avatar_lad.xml index 5944c9b1d..23d23b5c2 100644 --- a/indra/newview/character/avatar_lad.xml +++ b/indra/newview/character/avatar_lad.xml @@ -11843,6 +11843,17 @@ value_max="1"> + + + + diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 8f412d0b8..da48f89c8 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -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\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++) { diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 7a37a05f1..23c73b671 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -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(); diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index ed7165e50..44ce9885c 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -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 )); diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 44aac869e..cdf4a4c11 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -226,14 +226,14 @@ LLAgentWearables::sendAgentWearablesUpdateCallback::~sendAgentWearablesUpdateCal * @param todo Bitmask of actions to take on completion. */ LLAgentWearables::addWearableToAgentInventoryCallback::addWearableToAgentInventoryCallback( - LLPointer cb, LLWearableType::EType type, U32 index, LLViewerWearable* wearable, U32 todo) : + LLPointer 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"); } diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index b6738604d..794d656b3 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -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 mCB; + std::string mDescription; }; }; // LLAgentWearables diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp index 40b311a95..a1c60e921 100644 --- a/indra/newview/llagentwearablesfetch.cpp +++ b/indra/newview/llagentwearablesfetch.cpp @@ -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"); } } diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 3517a2293..e5a6356e5 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -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)) + LL_DEBUGS("Avatar") << "item_id " << item_id << llendl; + + if (!requestOperation(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; - - if (ll_frand()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 cb) +void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id, + LLPointer 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 cb = NULL): + LLDeferredCOFLinkObserver(const LLUUID& item_id, bool do_update, LLPointer 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 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 cb) +void LLAppearanceMgr::addCOFItemLink(const LLUUID &item_id, bool do_update, LLPointer 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 cb) + +void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item, bool do_update, LLPointer cb, const std::string description) { + std::string link_description = description; const LLViewerInventoryItem *vitem = dynamic_cast(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(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; igetLinkedUUID() == 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(); diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 2db333aa8..1718f6d5d 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -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,9 +148,12 @@ public: LLPointer cb); // Add COF link to individual item. - void addCOFItemLink(const LLUUID& item_id, bool do_update = true, LLPointer cb = NULL); - void addCOFItemLink(const LLInventoryItem *item, bool do_update = true, LLPointer cb = NULL); + void addCOFItemLink(const LLUUID& item_id, bool do_update = true, LLPointer cb = NULL, const std::string description = ""); + void addCOFItemLink(const LLInventoryItem *item, bool do_update = true, LLPointer 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); void removeCOFLinksOfType(LLWearableType::EType type); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index dd7255e21..af9e0032a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -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 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,7 +1840,8 @@ 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. if (!gLaunchFileOnQuit.empty()) @@ -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(); +} + diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 681cb505d..e28dda504 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -168,6 +168,10 @@ public: void addOnIdleCallback(const boost::function& 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. diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index afe3040c4..e0df21bb3 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -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; diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index e74bacaa8..d24d4a033 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -1277,6 +1277,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 @@ -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); } } diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 7236c8b12..1debd4209 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -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 ) diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index a8d36867b..a80003991 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -48,7 +48,7 @@ std::vector LLVolumeImplFlexible::sInstanceList; std::vector 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; diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp index d44e6f7cb..657ac452a 100644 --- a/indra/newview/llfloatercolorpicker.cpp +++ b/indra/newview/llfloatercolorpicker.cpp @@ -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" ) ) diff --git a/indra/newview/llfloatercustomize.cpp b/indra/newview/llfloatercustomize.cpp index f46daa693..0c3046680 100644 --- a/indra/newview/llfloatercustomize.cpp +++ b/indra/newview/llfloatercustomize.cpp @@ -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 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; diff --git a/indra/newview/llfloatercustomize.h b/indra/newview/llfloatercustomize.h index 56047db1e..39bc8ec67 100644 --- a/indra/newview/llfloatercustomize.h +++ b/indra/newview/llfloatercustomize.h @@ -117,6 +117,7 @@ private: public: void askToSaveIfDirty( boost::function cb ); + void saveCurrentWearables(); void switchToDefaultSubpart(); private: diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 37d795a49..c5ecc78b9 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -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); } } diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 98ed5cd6d..d792118e4 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -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")); } diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp index 77631c193..e7cdc51cc 100644 --- a/indra/newview/llmanip.cpp +++ b/indra/newview/llmanip.cpp @@ -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); + } } } diff --git a/indra/newview/llmorphview.cpp b/indra/newview/llmorphview.cpp index 0c92db3b6..bfef31156 100644 --- a/indra/newview/llmorphview.cpp +++ b/indra/newview/llmorphview.cpp @@ -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); } diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index e28d006a3..9442caa53 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -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); } } diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index ecc652645..f730984bf 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -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 diff --git a/indra/newview/llsimplestat.h b/indra/newview/llsimplestat.h new file mode 100644 index 000000000..9d7780c4f --- /dev/null +++ b/indra/newview/llsimplestat.h @@ -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 +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 & 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 diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 521c5e4f0..39d320a6e 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -224,6 +224,7 @@ // #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 LLStartUp::sStateWatcher(new LLEventStream("StartupState")); +boost::scoped_ptr 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() { diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 448a6792e..021cba775 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -33,7 +33,12 @@ #ifndef LL_LLSTARTUP_H #define LL_LLSTARTUP_H -class LLViewerTexture; +#include + +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 sStateWatcher; + static boost::scoped_ptr sPhases; }; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index accd96b42..17323d610 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -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 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 + diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index cbf8d1b50..f23961083 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -33,19 +33,24 @@ #ifndef LL_LLTEXTUREFETCH_H #define LL_LLTEXTUREFETCH_H +#include +#include + #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 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 diff --git a/indra/newview/lltoolfocus.cpp b/indra/newview/lltoolfocus.cpp index b98b6092f..568d20c23 100644 --- a/indra/newview/lltoolfocus.cpp +++ b/indra/newview/lltoolfocus.cpp @@ -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(); diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp new file mode 100644 index 000000000..ed768eb09 --- /dev/null +++ b/indra/newview/llviewerassetstats.cpp @@ -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: + * + * + * Test Plan: + * + * + * Jiras: + * + * + * 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 diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h new file mode 100644 index 000000000..3381c01ed --- /dev/null +++ b/indra/newview/llviewerassetstats.h @@ -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 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 > 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 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 diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index ffd44ed78..b0e9593ac 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -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); + } + } +} + diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index 7b85f55d7..ca9b9943f 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -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 diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 0bdee81c3..d80fd9713 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -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); } diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index f2c90c36c..0afebcbc3 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -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); diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 2a42b7661..55c3a980a 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -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, diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index be87807f1..d7ca39d5a 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -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"); diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 16f494715..2d20f8de3 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -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; +} diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index 264b02bf2..f7c7c0c1c 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -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 phase_map_t; + typedef std::map 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]; diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index c4ad62fb2..0f305c087 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -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 ; } diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index 0cbe896a6..294cfefb7 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -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) ; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index d57a05a46..06bca2c16 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -399,7 +399,24 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& } LLPointer 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 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,9 +935,8 @@ 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(); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index fee8c5253..1ac12e15c 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -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& ids) +{ + for (std::set::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 local_ids; + collectLocalTextureUUIDs(local_ids); + return allTexturesCompletelyDownloaded(local_ids); +} + +bool LLVOAvatar::allBakedTexturesCompletelyDownloaded() +{ + std::set 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 baked_ids; + collectBakedTextureUUIDs(baked_ids); + for (std::set::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 baked_ids; + collectBakedTextureUUIDs(baked_ids); + for (std::set::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& ids) +{ + S32 result = 0; + for (std::set::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& 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& 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& 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 baked_texture_ids; + collectBakedTextureUUIDs(baked_texture_ids); + S32 new_baked_mem = totalTextureMemForUUIDS(baked_texture_ids); + + std::set local_texture_ids; + collectLocalTextureUUIDs(local_texture_ids); + S32 new_local_mem = totalTextureMemForUUIDS(local_texture_ids); + + std::set 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::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::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 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 mParamWeights; + std::vector 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::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 ; - - if( is_first_appearance_message ) + LL_INFOS("Avatar") << avString() << "processAvatarAppearance start " << mID + << " first? " << is_first_appearance_message << " self? " << isSelf() << LL_ENDL; + + if (is_first_appearance_message ) { onFirstTEMessageReceived(); } @@ -7512,86 +8000,46 @@ 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 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); - } + 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(); - } - 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) - { - 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) + if (is_first_appearance_message || (param->getWeight() != newWeight)) { - 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; + params_changed = TRUE; + if(is_first_appearance_message) + { + param->setWeight(newWeight, FALSE); + } + else + { + interp_params = TRUE; + param->setAnimationTarget(newWeight, FALSE); + } } } + const S32 expected_tweakable_count = getVisualParamCountInGroup(VISUAL_PARAM_GROUP_TWEAKABLE); // don't worry about VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT + if (num_params != expected_tweakable_count) + { + 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 diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index cf9643373..1adf59ce8 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -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, 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& ids); + bool allTexturesCompletelyDownloaded(std::set& 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& ids); + void collectBakedTextureUUIDs(std::set& ids); + void collectTextureUUIDs(std::set& 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 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); diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 566f773b1..8ab29f500 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -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 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( 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 in_records, + std::vector by_fields, + std::string& val_field) +{ + LLSD result = LLSD::emptyArray(); + std::map accum; + for (std::vector::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::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::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 rez_counts; + LLVOAvatar::getNearbyRezzedStats(rez_counts); + for (std::vector::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 bucket_fields("timer_name","agent_id","is_self","grid_x","grid_y","is_using_server_bake"); + std::vector 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 diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 1cb09086e..3c0dba237 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -378,6 +378,13 @@ public: LLUUID mAvatarID; LLAvatarAppearanceDefines::ETextureIndex mIndex; }; + + LLTimer mTimeSinceLastRezMessage; + bool updateAvatarRezMetrics(bool force_send); + + std::vector 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 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 diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 2af2dc14b..d1cee520d 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -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))); diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 0ad2cfbda..487a28905 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -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* avatar_ids, std::vectorisDead() && !pVOAvatar->isSelf()) + + if (!pVOAvatar->isDead() && !pVOAvatar->isSelf() && !pVOAvatar->mIsDummy) { LLUUID uuid = pVOAvatar->getID(); if(!uuid.isNull()) From dbf646f266d79c001b164e241d1aa4f9de31e9e4 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 19 Feb 2013 01:52:29 -0600 Subject: [PATCH 10/26] More verbose fmodex llinfos output upon init. (will be useful when I add more output options.. if we keep fmodex) --- indra/llaudio/llaudioengine_fmodex.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index ba2b6615c..984e63ca9 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -288,13 +288,33 @@ bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) int r_numbuffers, r_samplerate, r_channels, r_bits; unsigned int r_bufferlength; char r_name[256]; + FMOD_SPEAKERMODE speaker_mode; mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers); mSystem->getSoftwareFormat(&r_samplerate, NULL, &r_channels, NULL, NULL, &r_bits); mSystem->getDriverInfo(0, r_name, 255, 0); + mSystem->getSpeakerMode(&speaker_mode); + std::string speaker_mode_str = "unknown"; + switch(speaker_mode) + { + #define SPEAKER_MODE_CASE(MODE) case MODE: speaker_mode_str = #MODE; break; + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_RAW) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_MONO) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_STEREO) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_QUAD) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_SURROUND) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_5POINT1) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_7POINT1) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_SRS5_1_MATRIX) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_MYEARS) + default:; + #undef SPEAKER_MODE_CASE + } + r_name[255] = '\0'; int latency = 1000.0 * r_bufferlength * r_numbuffers /r_samplerate; LL_INFOS("AppInit") << "FMOD device: "<< r_name << "\n" + << "Output mode: "<< speaker_mode_str << "\n" << "FMOD Ex parameters: " << r_samplerate << " Hz * " << r_channels << " * " < Date: Tue, 19 Feb 2013 08:40:12 -0500 Subject: [PATCH 11/26] Added the ever-requested marketplace tab to search floater (When on SL Grids, only) --- indra/newview/llfloaterdirectory.cpp | 25 +++++++++++++++++++ .../default/xui/en-us/floater_directory.xml | 11 ++++++++ 2 files changed, 36 insertions(+) diff --git a/indra/newview/llfloaterdirectory.cpp b/indra/newview/llfloaterdirectory.cpp index 128d7a379..9953d5d73 100644 --- a/indra/newview/llfloaterdirectory.cpp +++ b/indra/newview/llfloaterdirectory.cpp @@ -67,6 +67,29 @@ #include "hippogridmanager.h" +class LLPanelDirMarket : public LLPanelDirFind +{ +public: + LLPanelDirMarket(const std::string& name, LLFloaterDirectory* floater) + : LLPanelDirFind(name, floater, "market_browser") + {} + + /*virtual*/ void search(const std::string& url) + { + if (url.empty()) navigateToDefaultPage(); + } + + /*virtual*/ void navigateToDefaultPage() + { + if (mWebBrowser) mWebBrowser->navigateTo(getString("default_search_page")); + } + + static void* create(void* data) + { + return new LLPanelDirMarket("market_panel", static_cast(data)); + } +}; + LLFloaterDirectory* LLFloaterDirectory::sInstance = NULL; //static S32 LLFloaterDirectory::sOldSearchCount = 0; // debug @@ -110,6 +133,8 @@ LLFloaterDirectory::LLFloaterDirectory(const std::string& name) // web search and showcase only for SecondLife factory_map["find_all_panel"] = LLCallbackMap(createFindAll, this); factory_map["showcase_panel"] = LLCallbackMap(createShowcase, this); + if (!enableClassicAllSearch) + factory_map["market_panel"] = LLCallbackMap(LLPanelDirMarket::create, this); } if (enableClassicAllSearch) diff --git a/indra/newview/skins/default/xui/en-us/floater_directory.xml b/indra/newview/skins/default/xui/en-us/floater_directory.xml index f034425a6..02c9f2a83 100644 --- a/indra/newview/skins/default/xui/en-us/floater_directory.xml +++ b/indra/newview/skins/default/xui/en-us/floater_directory.xml @@ -686,6 +686,17 @@ To buy direct, visit the land and click on the place name in the title bar. bottom_delta="-20" drop_shadow_visible="true" follows="left|bottom" font="SansSerifSmall" h_pad="0" halign="left" height="16" left="4" mouse_opaque="true" name="result_text" v_pad="0" width="328" /> + + +