Add bulk speaker updates. Sped up speaker fetching by disabling list sorting during bulk updates.

This commit is contained in:
Shyotl
2018-02-07 21:18:35 -06:00
parent fa628ce4d1
commit 30df7dacde
6 changed files with 177 additions and 74 deletions

View File

@@ -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()

View File

@@ -322,6 +322,7 @@ public:
BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; }
BOOL hasSortOrder() const;
void clearSortOrder();
void setSortEnabled(bool sort);
template<typename T> S32 selectMultiple(const std::vector<T>& vec)
{
@@ -423,6 +424,7 @@ private:
bool mDisplayColumnHeaders;
bool mColumnsDirty;
bool mColumnWidthsDirty;
bool mSortEnabled;
mutable item_list mItemList;

View File

@@ -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<LLOldEvents::LLEvent> 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<LLOldEvents::
return mParent.onSpeakerMuteEvent(event, userdata);
}
bool LLParticipantList::SpeakerBatchBeginListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
{
mParent.onSpeakerBatchBeginEvent();
return true;
}
bool LLParticipantList::SpeakerBatchEndListener::handleEvent(LLPointer<LLOldEvents::LLEvent> 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)
{

View File

@@ -76,6 +76,8 @@ protected:
bool onClearListEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
//bool onModeratorUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
bool onSpeakerMuteEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
void onSpeakerBatchBeginEvent();
void onSpeakerBatchEndEvent();
/**
* List of listeners implementing LLOldEvents::LLSimpleListener.
@@ -126,6 +128,20 @@ protected:
/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
};
class SpeakerBatchBeginListener : public BaseSpeakerListener
{
public:
SpeakerBatchBeginListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {}
/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
};
class SpeakerBatchEndListener : public BaseSpeakerListener
{
public:
SpeakerBatchEndListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {}
/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
};
/**
* Menu used in the participant list.
class LLParticipantListMenu : public LLListContextMenu
@@ -202,6 +218,8 @@ private:
LLPointer<SpeakerClearListener> mSpeakerClearListener;
//LLPointer<SpeakerModeratorUpdateListener> mSpeakerModeratorListener;
LLPointer<SpeakerMuteListener> mSpeakerMuteListener;
LLPointer<SpeakerBatchBeginListener> mSpeakerBatchBeginListener;
LLPointer<SpeakerBatchEndListener> mSpeakerBatchEndListener;
validate_speaker_callback_t mValidateSpeakerCallback;
};

View File

@@ -338,7 +338,20 @@ LLSpeakerMgr::~LLSpeakerMgr()
delete mSpeakerDelayRemover;
}
LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type)
void LLSpeakerMgr::setSpeakers(const std::vector<LLSpeakerMgr::speaker_entry_t>& 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<LLSpeaker> 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<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::strin
}
LLPointer<LLSpeaker> 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<LLUUID> participants;
LLVoiceClient::getInstance()->getParticipantList(participants);
// If we are, add all voice client participants to our list of known speakers
std::vector<speaker_entry_t> speakers;
speakers.reserve(participants.size());
for (std::set<LLUUID>::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<speaker_entry_t> 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<LLSpeaker> 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<speaker_entry_t> 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<LLSpeaker> 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<LLSpeaker> 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<speaker_entry_t> 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<LLSpeaker> 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<speaker_entry_t> 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)

View File

@@ -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<speaker_entry_t>& speakers);
LLPointer<LLSpeaker> 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();