From 30df7dacdefbe34018f53c5e8440bfc0b0cbd48c Mon Sep 17 00:00:00 2001 From: Shyotl Date: Wed, 7 Feb 2018 21:18:35 -0600 Subject: [PATCH] Add bulk speaker updates. Sped up speaker fetching by disabling list sorting during bulk updates. --- indra/llui/llscrolllistctrl.cpp | 13 ++- indra/llui/llscrolllistctrl.h | 2 + indra/newview/llparticipantlist.cpp | 26 ++++- indra/newview/llparticipantlist.h | 18 +++ indra/newview/llspeakers.cpp | 168 ++++++++++++++++------------ indra/newview/llspeakers.h | 24 +++- 6 files changed, 177 insertions(+), 74 deletions(-) diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 67e4ff4d1..788adfa70 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -125,6 +125,7 @@ LLScrollListCtrl::LLScrollListCtrl(const std::string& name, const LLRect& rect, mNeedsScroll(false), mCanSelect(true), mColumnsDirty(false), + mSortEnabled(true), mMaxItemCount(INT_MAX), mMaxContentWidth(0), mBorderThickness( 2 ), @@ -2301,6 +2302,16 @@ BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending) } } +void LLScrollListCtrl::setSortEnabled(bool sort) +{ + bool update = sort && !mSortEnabled; + mSortEnabled = sort; + if (update) + { + updateSort(); + } +} + S32 LLScrollListCtrl::getLinesPerPage() { //if mPageLines is NOT provided display all item @@ -2942,7 +2953,7 @@ std::string LLScrollListCtrl::getSortColumnName() BOOL LLScrollListCtrl::hasSortOrder() const { - return !mSortColumns.empty(); + return mSortEnabled && !mSortColumns.empty(); } void LLScrollListCtrl::clearSortOrder() diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 68905327c..e10a29373 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -322,6 +322,7 @@ public: BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; } BOOL hasSortOrder() const; void clearSortOrder(); + void setSortEnabled(bool sort); template S32 selectMultiple(const std::vector& vec) { @@ -423,6 +424,7 @@ private: bool mDisplayColumnHeaders; bool mColumnsDirty; bool mColumnWidthsDirty; + bool mSortEnabled; mutable item_list mItemList; diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index d9b34ca93..499c014e3 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -58,10 +58,13 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, mSpeakerClearListener = new SpeakerClearListener(*this); //mSpeakerModeratorListener = new SpeakerModeratorUpdateListener(*this); mSpeakerMuteListener = new SpeakerMuteListener(*this); - + mSpeakerBatchBeginListener = new SpeakerBatchBeginListener(*this); + mSpeakerBatchEndListener = new SpeakerBatchEndListener(*this); mSpeakerMgr->addListener(mSpeakerAddListener, "add"); mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove"); mSpeakerMgr->addListener(mSpeakerClearListener, "clear"); + mSpeakerMgr->addListener(mSpeakerBatchBeginListener, "batch_begin"); + mSpeakerMgr->addListener(mSpeakerBatchEndListener, "batch_end"); //mSpeakerMgr->addListener(mSpeakerModeratorListener, "update_moderator"); } @@ -378,6 +381,16 @@ bool LLParticipantList::onSpeakerMuteEvent(LLPointer event return true; } +void LLParticipantList::onSpeakerBatchBeginEvent() +{ + mAvatarList->setSortEnabled(false); +} + +void LLParticipantList::onSpeakerBatchEndEvent() +{ + mAvatarList->setSortEnabled(true); +} + void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) { //if (mExcludeAgent && gAgent.getID() == avatar_id) return; @@ -468,6 +481,17 @@ bool LLParticipantList::SpeakerMuteListener::handleEvent(LLPointer event, const LLSD& userdata) +{ + mParent.onSpeakerBatchBeginEvent(); + return true; +} +bool LLParticipantList::SpeakerBatchEndListener::handleEvent(LLPointer event, const LLSD& userdata) +{ + mParent.onSpeakerBatchEndEvent(); + return true; +} + // Singu Note: The following functions are actually of the LLParticipantListMenu class, but we haven't married lists with menus yet. void LLParticipantList::toggleAllowTextChat(const LLSD& userdata) { diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 1177b778a..17be7bc78 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -76,6 +76,8 @@ protected: bool onClearListEvent(LLPointer event, const LLSD& userdata); //bool onModeratorUpdateEvent(LLPointer event, const LLSD& userdata); bool onSpeakerMuteEvent(LLPointer event, const LLSD& userdata); + void onSpeakerBatchBeginEvent(); + void onSpeakerBatchEndEvent(); /** * List of listeners implementing LLOldEvents::LLSimpleListener. @@ -126,6 +128,20 @@ protected: /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); }; + class SpeakerBatchBeginListener : public BaseSpeakerListener + { + public: + SpeakerBatchBeginListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); + }; + + class SpeakerBatchEndListener : public BaseSpeakerListener + { + public: + SpeakerBatchEndListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); + }; + /** * Menu used in the participant list. class LLParticipantListMenu : public LLListContextMenu @@ -202,6 +218,8 @@ private: LLPointer mSpeakerClearListener; //LLPointer mSpeakerModeratorListener; LLPointer mSpeakerMuteListener; + LLPointer mSpeakerBatchBeginListener; + LLPointer mSpeakerBatchEndListener; validate_speaker_callback_t mValidateSpeakerCallback; }; diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 8f989454c..dc352bc18 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -338,7 +338,20 @@ LLSpeakerMgr::~LLSpeakerMgr() delete mSpeakerDelayRemover; } -LLPointer LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) +void LLSpeakerMgr::setSpeakers(const std::vector& speakers) +{ + if (!speakers.empty()) + { + fireEvent(new LLOldEvents::LLEvent(this), "batch_begin"); + for (auto entry : speakers) + { + setSpeaker(entry.id, entry.name, entry.status, entry.type, entry.moderator, entry.moderator_muted_text); + } + fireEvent(new LLOldEvents::LLEvent(this), "batch_end"); + } +} + +LLPointer LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type, bool moderator, bool moderator_muted_text) { LLUUID session_id = getSessionID(); if (id.isNull() || (id == session_id)) @@ -347,34 +360,37 @@ LLPointer LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::strin } LLPointer speakerp; - if (mSpeakers.find(id) == mSpeakers.end()) + auto it = mSpeakers.find(id); + if (it == mSpeakers.end() || it->second.isNull()) { speakerp = new LLSpeaker(id, name, type); speakerp->mStatus = status; + speakerp->mIsModerator = moderator; + speakerp->mModeratorMutedText = moderator_muted_text; mSpeakers.insert(std::make_pair(speakerp->mID, speakerp)); mSpeakersSorted.push_back(speakerp); - LL_DEBUGS("Speakers") << "Added speaker " << id << LL_ENDL; fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "add"); } else { - speakerp = findSpeaker(id); - if (speakerp.notNull()) + speakerp = it->second; + bool update_moderator = (bool)speakerp->mIsModerator != moderator; + + // keep highest priority status (lowest value) instead of overriding current value + speakerp->mStatus = llmin(speakerp->mStatus, status); + speakerp->mIsModerator = moderator; + speakerp->mModeratorMutedText = moderator_muted_text; + // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id + // we need to override speakers that we think are objects when we find out they are really + // residents + if (type == LLSpeaker::SPEAKER_AGENT) { - // keep highest priority status (lowest value) instead of overriding current value - speakerp->mStatus = llmin(speakerp->mStatus, status); - // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id - // we need to override speakers that we think are objects when we find out they are really - // residents - if (type == LLSpeaker::SPEAKER_AGENT) - { - speakerp->mType = LLSpeaker::SPEAKER_AGENT; - speakerp->lookupName(); - } + speakerp->mType = LLSpeaker::SPEAKER_AGENT; + speakerp->lookupName(); } - else + if (update_moderator) { - LL_WARNS("Speakers") << "Speaker " << id << " not found" << LL_ENDL; + fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator"); } } @@ -534,13 +550,17 @@ void LLSpeakerMgr::updateSpeakerList() std::set participants; LLVoiceClient::getInstance()->getParticipantList(participants); // If we are, add all voice client participants to our list of known speakers + std::vector speakers; + speakers.reserve(participants.size()); for (std::set::iterator participant_it = participants.begin(); participant_it != participants.end(); ++participant_it) { - setSpeaker(*participant_it, - LLVoiceClient::getInstance()->getDisplayName(*participant_it), - LLSpeaker::STATUS_VOICE_ACTIVE, - (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL)); + speakers.emplace_back(speaker_entry_t( + *participant_it, + LLSpeaker::STATUS_VOICE_ACTIVE, + LLVoiceClient::getInstance()->getDisplayName(*participant_it), + (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL))); } + setSpeakers(speakers); } else { @@ -569,10 +589,11 @@ void LLSpeakerMgr::updateSpeakerList() } else if (gdatap && gdatap->isMemberDataComplete() && !gdatap->mMembers.empty()) { + std::vector speakers; + speakers.reserve(gdatap->mMembers.size()); + // Add group members when we get the complete list (note: can take a while before we get that list) LLGroupMgrGroupData::member_list_t::iterator member_it = gdatap->mMembers.begin(); - const S32 load_group_max_members = gSavedSettings.getS32("ChatLoadGroupMaxMembers"); - S32 updated = 0; while (member_it != gdatap->mMembers.end()) { LLGroupMemberData* member = member_it->second; @@ -581,16 +602,16 @@ void LLSpeakerMgr::updateSpeakerList() const std::string& localized_online(); if ((member->getOnlineStatus() == localized_online()) && (mSpeakers.find(id) == mSpeakers.end())) { - LLPointer speakerp = setSpeaker(id, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT); - speakerp->mIsModerator = ((member->getAgentPowers() & GP_SESSION_MODERATOR) == GP_SESSION_MODERATOR); - updated++; + speakers.emplace_back(speaker_entry_t( + id, + LLSpeaker::STATUS_VOICE_ACTIVE, + "", + LLSpeaker::SPEAKER_AGENT, + (member->getAgentPowers() & GP_SESSION_MODERATOR) == GP_SESSION_MODERATOR)); } ++member_it; - // Limit the number of "manually updated" participants to a reasonable number to avoid severe fps drop - // *TODO : solve the perf issue of having several hundreds of widgets in the conversation list - if (updated >= load_group_max_members) - break; } + setSpeakers(speakers); mSpeakerListUpdated = true; } } @@ -617,7 +638,7 @@ void LLSpeakerMgr::updateSpeakerList() } } // Always add the current agent (it has to be there...). Will do nothing if already there. - setSpeaker(gAgentID, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT); + setSpeaker(gAgentID, "", LLSpeaker::STATUS_VOICE_ACTIVE); } void LLSpeakerMgr::setSpeakerNotInChannel(LLSpeaker* speakerp) @@ -737,6 +758,7 @@ void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers) { if ( !speakers.isMap() ) return; + std::vector speakerentries; if ( speakers.has("agent_info") && speakers["agent_info"].isMap() ) { LLSD::map_const_iterator speaker_it; @@ -744,26 +766,21 @@ void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers) speaker_it != speakers["agent_info"].endMap(); ++speaker_it) { - LLUUID agent_id(speaker_it->first); - - LLPointer speakerp = setSpeaker( - agent_id, - LLStringUtil::null, - LLSpeaker::STATUS_TEXT_ONLY); - - if ( speaker_it->second.isMap() ) + bool moderator = false; + bool moderator_muted = false; + if (speaker_it->second.isMap()) { - BOOL is_moderator = speakerp->mIsModerator; - speakerp->mIsModerator = speaker_it->second["is_moderator"]; - speakerp->mModeratorMutedText = - speaker_it->second["mutes"]["text"]; - // Fire event only if moderator changed - if ( is_moderator != speakerp->mIsModerator ) - { - LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << LL_ENDL; - fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator"); - } + moderator = speaker_it->second["is_moderator"]; + moderator_muted = speaker_it->second["mutes"]["text"]; } + speakerentries.emplace_back( + LLUUID(speaker_it->first), + LLSpeaker::STATUS_TEXT_ONLY, + "", + LLSpeaker::SPEAKER_AGENT, + moderator, + moderator_muted + ); } } else if ( speakers.has("agents" ) && speakers["agents"].isArray() ) @@ -775,22 +792,20 @@ void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers) speaker_it != speakers["agents"].endArray(); ++speaker_it) { - const LLUUID agent_id = (*speaker_it).asUUID(); - - LLPointer speakerp = setSpeaker( - agent_id, - LLStringUtil::null, - LLSpeaker::STATUS_TEXT_ONLY); + speakerentries.emplace_back((*speaker_it).asUUID()); } } + LLSpeakerMgr::setSpeakers(speakerentries); } void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) { if ( !update.isMap() ) return; + std::vector speakerentries; if ( update.has("agent_updates") && update["agent_updates"].isMap() ) { + LLSD::map_const_iterator update_it; for( update_it = update["agent_updates"].beginMap(); @@ -800,8 +815,11 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) LLUUID agent_id(update_it->first); LLPointer speakerp = findSpeaker(agent_id); - LLSD agent_data = update_it->second; + bool new_speaker = false; + bool moderator = false; + bool moderator_muted = false; + LLSD agent_data = update_it->second; if (agent_data.isMap() && agent_data.has("transition")) { if (agent_data["transition"].asString() == "LEAVE" && speakerp.notNull()) @@ -811,15 +829,17 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) else if (agent_data["transition"].asString() == "ENTER") { // add or update speaker - speakerp = setSpeaker(agent_id); + new_speaker = true; } else { LL_WARNS() << "bad membership list update from 'agent_updates' for agent " << agent_id << ", transition " << ll_print_sd(agent_data["transition"]) << LL_ENDL; } } - - if (speakerp.isNull()) continue; + if (speakerp.isNull() && !new_speaker) + { + continue; + } // should have a valid speaker from this point on if (agent_data.isMap() && agent_data.has("info")) @@ -828,21 +848,21 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) if (agent_info.has("is_moderator")) { - BOOL is_moderator = speakerp->mIsModerator; - speakerp->mIsModerator = agent_info["is_moderator"]; - // Fire event only if moderator changed - if ( is_moderator != speakerp->mIsModerator ) - { - LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << LL_ENDL; - fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator"); - } + moderator = agent_info["is_moderator"]; } - if (agent_info.has("mutes")) { - speakerp->mModeratorMutedText = agent_info["mutes"]["text"]; + moderator_muted = agent_info["mutes"]["text"]; } } + speakerentries.emplace_back( + agent_id, + LLSpeaker::STATUS_TEXT_ONLY, + "", + LLSpeaker::SPEAKER_AGENT, + moderator, + moderator_muted + ); } } else if ( update.has("updates") && update["updates"].isMap() ) @@ -864,7 +884,9 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) else if ( agent_transition == "ENTER") { // add or update speaker - speakerp = setSpeaker(agent_id); + speakerentries.emplace_back( + agent_id + ); } else { @@ -872,6 +894,7 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) } } } + LLSpeakerMgr::setSpeakers(speakerentries); } void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id) @@ -1039,10 +1062,13 @@ void LLLocalSpeakerMgr::updateSpeakerList() // pick up non-voice speakers in chat range uuid_vec_t avatar_ids; LLWorld::getInstance()->getAvatars(&avatar_ids, nullptr, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS); + std::vector speakers; + speakers.reserve(avatar_ids.size()); for (const auto& id : avatar_ids) { - setSpeaker(id); + speakers.emplace_back(id); } + setSpeakers(speakers); // check if text only speakers have moved out of chat range for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index 107749224..a2f05f71a 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -230,10 +230,32 @@ public: void update(BOOL resort_ok); void setSpeakerTyping(const LLUUID& speaker_id, BOOL typing); void speakerChatted(const LLUUID& speaker_id); + + struct speaker_entry_t + { + speaker_entry_t(const LLUUID& id, + LLSpeaker::ESpeakerStatus status = LLSpeaker::STATUS_TEXT_ONLY, + const std::string& name = LLStringUtil::null, + LLSpeaker::ESpeakerType type = LLSpeaker::SPEAKER_AGENT, + const bool moderator = false, + const bool moderator_muted_text = false) : + id(id), name(name), status(status), type(type), moderator(moderator), moderator_muted_text(moderator_muted_text) + {} + const LLUUID id; + const std::string name; + const LLSpeaker::ESpeakerStatus status; + const LLSpeaker::ESpeakerType type; + const bool moderator; + const bool moderator_muted_text; + }; + + void setSpeakers(const std::vector& speakers); LLPointer setSpeaker(const LLUUID& id, const std::string& name = LLStringUtil::null, LLSpeaker::ESpeakerStatus status = LLSpeaker::STATUS_TEXT_ONLY, - LLSpeaker::ESpeakerType = LLSpeaker::SPEAKER_AGENT); + LLSpeaker::ESpeakerType = LLSpeaker::SPEAKER_AGENT, + bool moderator = false, + bool moderator_muted_text = false); BOOL isVoiceActive();