use PrioritySortUtil for outgoing avatar updates

This commit is contained in:
Andrew Meadows 2017-11-20 14:23:56 -08:00
parent d65101c4e9
commit e068eb879c
3 changed files with 61 additions and 34 deletions

View file

@ -110,7 +110,7 @@ public:
bool getRequestsDomainListData() { return _requestsDomainListData; } bool getRequestsDomainListData() { return _requestsDomainListData; }
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
ViewFrustum getViewFrustom() const { return _currentViewFrustum; } ViewFrustum getViewFrustum() const { return _currentViewFrustum; }
quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) { quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) {
quint64 result = 0; quint64 result = 0;

View file

@ -22,6 +22,7 @@
#include <NodeList.h> #include <NodeList.h>
#include <Node.h> #include <Node.h>
#include <OctreeConstants.h> #include <OctreeConstants.h>
#include <PrioritySortUtil.h>
#include <udt/PacketHeaders.h> #include <udt/PacketHeaders.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <StDev.h> #include <StDev.h>
@ -32,6 +33,10 @@
#include "AvatarMixerClientData.h" #include "AvatarMixerClientData.h"
#include "AvatarMixerSlave.h" #include "AvatarMixerSlave.h"
namespace PrioritySortUtil {
// we declare this callback here but override it later
std::function<uint64_t(const AvatarSharedPointer&)> getAvatarAgeCallback = [] (const AvatarSharedPointer& avatar) { return 0; };
}
void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) { void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
_begin = begin; _begin = begin;
@ -185,7 +190,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes
// for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes // for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes
QList<AvatarSharedPointer> avatarList; std::vector<AvatarSharedPointer> avatarsToSort;
std::unordered_map<AvatarSharedPointer, SharedNodePointer> avatarDataToNodes; std::unordered_map<AvatarSharedPointer, SharedNodePointer> avatarDataToNodes;
std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) {
@ -195,36 +200,59 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData()); const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer();
avatarList << otherAvatar; avatarsToSort.push_back(otherAvatar);
avatarDataToNodes[otherAvatar] = otherNode; avatarDataToNodes[otherAvatar] = otherNode;
} }
}); });
AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); // now that we've assembled the avatarDataToNodes map we can replace PrioritySortUtil::getAvatarAgeCallback
ViewFrustum cameraView = nodeData->getViewFrustom(); // with the true implementation
std::priority_queue<AvatarPriority> sortedAvatars; PrioritySortUtil::getAvatarAgeCallback = [&] (const AvatarSharedPointer& avatar) {
AvatarData::sortAvatars(avatarList, cameraView, sortedAvatars,
[&](AvatarSharedPointer avatar)->uint64_t {
auto avatarNode = avatarDataToNodes[avatar]; auto avatarNode = avatarDataToNodes[avatar];
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
return nodeData->getLastBroadcastTime(avatarNode->getUUID()); return nodeData->getLastBroadcastTime(avatarNode->getUUID());
}, [&](AvatarSharedPointer avatar)->float{ };
glm::vec3 nodeBoxHalfScale = (avatar->getWorldPosition() - avatar->getGlobalBoundingBoxCorner() * avatar->getSensorToWorldScale());
class SortableAvatar: public PrioritySortUtil::Sortable {
public:
SortableAvatar() = delete;
SortableAvatar(const AvatarSharedPointer& avatar) : _avatar(avatar) {}
glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }
float getRadius() const override {
glm::vec3 nodeBoxHalfScale = (_avatar->getWorldPosition() - _avatar->getGlobalBoundingBoxCorner() * _avatar->getSensorToWorldScale());
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z));
}, [&](AvatarSharedPointer avatar)->bool { }
uint64_t getTimestamp() const override {
// use the callback implemented above
return PrioritySortUtil::getAvatarAgeCallback(_avatar);
}
const AvatarSharedPointer& getAvatar() const { return _avatar; }
private:
AvatarSharedPointer _avatar;
};
// prepare to sort
ViewFrustum cameraView = nodeData->getViewFrustum();
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(cameraView);
// ignore or sort
const AvatarSharedPointer& thisAvatar = nodeData->getAvatarSharedPointer();
for (size_t i = 0; i < avatarsToSort.size(); ++i) {
const AvatarSharedPointer& avatar = avatarsToSort[i];
if (avatar == thisAvatar) { if (avatar == thisAvatar) {
return true; // ignore ourselves... // don't echo updates to self
continue;
} }
bool shouldIgnore = false; bool shouldIgnore = false;
// We ignore other nodes for a couple of reasons:
// We will also ignore other nodes for a couple of different reasons:
// 1) ignore bubbles and ignore specific node // 1) ignore bubbles and ignore specific node
// 2) the node hasn't really updated it's frame data recently, this can // 2) the node hasn't really updated it's frame data recently, this can
// happen if for example the avatar is connected on a desktop and sending // happen if for example the avatar is connected on a desktop and sending
// updates at ~30hz. So every 3 frames we skip a frame. // updates at ~30hz. So every 3 frames we skip a frame.
auto avatarNode = avatarDataToNodes[avatar];
auto avatarNode = avatarDataToNodes[avatar];
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData()); const AvatarMixerClientData* avatarNodeData = reinterpret_cast<const AvatarMixerClientData*>(avatarNode->getLinkedData());
@ -240,7 +268,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|| (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) {
shouldIgnore = true; shouldIgnore = true;
} else { } else {
// Check to see if the space bubble is enabled // Check to see if the space bubble is enabled
// Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored // Don't bother with these checks if the other avatar has their bubble enabled and we're gettingAnyIgnored
if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) { if (node->isIgnoreRadiusEnabled() || (avatarNode->isIgnoreRadiusEnabled() && !getsAnyIgnored)) {
@ -267,8 +294,6 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID());
} }
} }
quint64 endIgnoreCalculation = usecTimestampNow();
_stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
if (!shouldIgnore) { if (!shouldIgnore) {
AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID());
@ -292,20 +317,21 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
++numAvatarsWithSkippedFrames; ++numAvatarsWithSkippedFrames;
} }
} }
return shouldIgnore; quint64 endIgnoreCalculation = usecTimestampNow();
}); _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation);
if (!shouldIgnore) {
// sort this one for later
sortedAvatars.push(SortableAvatar(avatar));
}
}
// loop through our sorted avatars and allocate our bandwidth to them accordingly // loop through our sorted avatars and allocate our bandwidth to them accordingly
int avatarRank = 0;
// this is overly conservative, because it includes some avatars we might not consider
int remainingAvatars = (int)sortedAvatars.size(); int remainingAvatars = (int)sortedAvatars.size();
while (!sortedAvatars.empty()) { while (!sortedAvatars.empty()) {
AvatarPriority sortData = sortedAvatars.top(); const auto& avatarData = sortedAvatars.top().getAvatar();
sortedAvatars.pop(); sortedAvatars.pop();
const auto& avatarData = sortData.avatar;
avatarRank++;
remainingAvatars--; remainingAvatars--;
auto otherNode = avatarDataToNodes[avatarData]; auto otherNode = avatarDataToNodes[avatarData];
@ -332,10 +358,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow()); nodeData->setLastBroadcastTime(otherNode->getUUID(), usecTimestampNow());
} }
// determine if avatar is in view which determines how much data to send
glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition();
// determine if avatar is in view, to determine how much data to include...
glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale(); glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f * otherAvatar->getSensorToWorldScale();
AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale);
bool isInView = nodeData->otherAvatarInView(otherNodeBox); bool isInView = nodeData->otherAvatarInView(otherNodeBox);
@ -412,7 +436,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
quint64 endAvatarDataPacking = usecTimestampNow(); quint64 endAvatarDataPacking = usecTimestampNow();
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
}; }
quint64 startPacketSending = usecTimestampNow(); quint64 startPacketSending = usecTimestampNow();

View file

@ -12,6 +12,9 @@
#define hifi_PrioritySortUtil_h #define hifi_PrioritySortUtil_h
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <queue>
#include "NumericalConstants.h"
#include "ViewFrustum.h" #include "ViewFrustum.h"
/* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use: /* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use: