mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 10:48:15 +02:00
use PrioritySortUtil for outgoing avatar updates
This commit is contained in:
parent
d65101c4e9
commit
e068eb879c
3 changed files with 61 additions and 34 deletions
|
@ -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;
|
||||||
|
|
|
@ -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,
|
auto avatarNode = avatarDataToNodes[avatar];
|
||||||
[&](AvatarSharedPointer avatar)->uint64_t {
|
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
|
||||||
auto avatarNode = avatarDataToNodes[avatar];
|
return nodeData->getLastBroadcastTime(avatarNode->getUUID());
|
||||||
assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map
|
};
|
||||||
return nodeData->getLastBroadcastTime(avatarNode->getUUID());
|
|
||||||
}, [&](AvatarSharedPointer avatar)->float{
|
class SortableAvatar: public PrioritySortUtil::Sortable {
|
||||||
glm::vec3 nodeBoxHalfScale = (avatar->getWorldPosition() - avatar->getGlobalBoundingBoxCorner() * avatar->getSensorToWorldScale());
|
public:
|
||||||
return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z));
|
SortableAvatar() = delete;
|
||||||
}, [&](AvatarSharedPointer avatar)->bool {
|
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));
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in a new issue