mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-07 20:22:27 +02:00
Follow dynamic updates to hero zones; make reserved fraction a domain setting
This commit is contained in:
parent
4ac25121e0
commit
a1660dad95
9 changed files with 104 additions and 29 deletions
|
@ -253,10 +253,26 @@ void AvatarMixer::start() {
|
|||
|
||||
int lockWait, nodeTransform, functor;
|
||||
|
||||
// Set our query each frame
|
||||
{
|
||||
_entityViewer.queryOctree();
|
||||
}
|
||||
|
||||
// Dirty the hero status if there's been an entity change.
|
||||
{
|
||||
if (_dirtyHeroStatus) {
|
||||
_dirtyHeroStatus = false;
|
||||
nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) {
|
||||
std::for_each(cbegin, cend, [](const SharedNodePointer& node) {
|
||||
if (node->getType() == NodeType::Agent) {
|
||||
auto& avatar = static_cast<AvatarMixerClientData*>(node->getLinkedData())->getAvatar();
|
||||
avatar.setNeedsHeroCheck();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Allow nodes to process any pending/queued packets across our worker threads
|
||||
{
|
||||
auto start = usecTimestampNow();
|
||||
|
@ -973,19 +989,31 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
|||
{
|
||||
const QString CONNECTION_RATE = "connection_rate";
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto defaultConnectionRate = nodeList->getMaxConnectionRate();
|
||||
int connectionRate = avatarMixerGroupObject[CONNECTION_RATE].toInt((int)defaultConnectionRate);
|
||||
nodeList->setMaxConnectionRate(connectionRate);
|
||||
bool success;
|
||||
int connectionRate = avatarMixerGroupObject[CONNECTION_RATE].toString().toInt(&success);
|
||||
if (success) {
|
||||
nodeList->setMaxConnectionRate(connectionRate);
|
||||
}
|
||||
}
|
||||
|
||||
{ // Fraction of downstream bandwidth reserved for 'hero' avatars:
|
||||
static const QString PRIORITY_FRACTION_KEY = "priority_fraction";
|
||||
if (avatarMixerGroupObject.contains(PRIORITY_FRACTION_KEY)) {
|
||||
bool isDouble = avatarMixerGroupObject[PRIORITY_FRACTION_KEY].isDouble();
|
||||
float priorityFraction = float(avatarMixerGroupObject[PRIORITY_FRACTION_KEY].toDouble());
|
||||
_slavePool.setPriorityReservedFraction(std::min(std::max(0.0f, priorityFraction), 1.0f));
|
||||
qCDebug(avatars) << "Avatar mixer reserving" << priorityFraction << "of bandwidth for priority avatars";
|
||||
}
|
||||
}
|
||||
|
||||
const QString AVATARS_SETTINGS_KEY = "avatars";
|
||||
|
||||
static const QString MIN_HEIGHT_OPTION = "min_avatar_height";
|
||||
float settingMinHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT);
|
||||
float settingMinHeight = avatarMixerGroupObject[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT);
|
||||
_domainMinimumHeight = glm::clamp(settingMinHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT);
|
||||
|
||||
static const QString MAX_HEIGHT_OPTION = "max_avatar_height";
|
||||
float settingMaxHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT);
|
||||
float settingMaxHeight = avatarMixerGroupObject[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT);
|
||||
_domainMaximumHeight = glm::clamp(settingMaxHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT);
|
||||
|
||||
// make sure that the domain owner didn't flip min and max
|
||||
|
@ -997,11 +1025,11 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
|||
<< "and a maximum avatar height of" << _domainMaximumHeight;
|
||||
|
||||
static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist";
|
||||
_slaveSharedData.skeletonURLWhitelist = domainSettings[AVATARS_SETTINGS_KEY].toObject()[AVATAR_WHITELIST_OPTION]
|
||||
_slaveSharedData.skeletonURLWhitelist = avatarMixerGroupObject[AVATAR_WHITELIST_OPTION]
|
||||
.toString().split(',', QString::KeepEmptyParts);
|
||||
|
||||
static const QString REPLACEMENT_AVATAR_OPTION = "replacement_avatar";
|
||||
_slaveSharedData.skeletonReplacementURL = domainSettings[AVATARS_SETTINGS_KEY].toObject()[REPLACEMENT_AVATAR_OPTION]
|
||||
_slaveSharedData.skeletonReplacementURL = avatarMixerGroupObject[REPLACEMENT_AVATAR_OPTION]
|
||||
.toString();
|
||||
|
||||
if (_slaveSharedData.skeletonURLWhitelist.count() == 1 && _slaveSharedData.skeletonURLWhitelist[0].isEmpty()) {
|
||||
|
@ -1018,9 +1046,12 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) {
|
|||
|
||||
void AvatarMixer::setupEntityQuery() {
|
||||
_entityViewer.init();
|
||||
EntityTreePointer entityTree = _entityViewer.getTree();
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||
DependencyManager::set<AssignmentParentFinder>(_entityViewer.getTree());
|
||||
_slaveSharedData.entityTree = _entityViewer.getTree();
|
||||
DependencyManager::set<AssignmentParentFinder>(entityTree);
|
||||
|
||||
connect(entityTree.get(), &EntityTree::addingEntityPointer, this, &AvatarMixer::entityAdded);
|
||||
connect(entityTree.get(), &EntityTree::deletingEntityPointer, this, &AvatarMixer::entityChange);
|
||||
|
||||
// ES query: {"avatarPriority": true, "type": "Zone"}
|
||||
QJsonObject priorityZoneQuery;
|
||||
|
@ -1028,6 +1059,7 @@ void AvatarMixer::setupEntityQuery() {
|
|||
priorityZoneQuery["type"] = "Zone";
|
||||
|
||||
_entityViewer.getOctreeQuery().setJSONParameters(priorityZoneQuery);
|
||||
_slaveSharedData.entityTree = entityTree;
|
||||
}
|
||||
|
||||
void AvatarMixer::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
|
@ -1064,6 +1096,25 @@ void AvatarMixer::handleOctreePacket(QSharedPointer<ReceivedMessage> message, Sh
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::entityAdded(EntityItem* entity) {
|
||||
if (entity->getType() == EntityTypes::Zone) {
|
||||
_dirtyHeroStatus = true;
|
||||
entity->registerChangeHandler([this](const EntityItemID& entityItemID) {
|
||||
this->entityChange();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::entityRemoved(EntityItem * entity) {
|
||||
if (entity->getType() == EntityTypes::Zone) {
|
||||
_dirtyHeroStatus = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::entityChange() {
|
||||
_dirtyHeroStatus = true;
|
||||
}
|
||||
|
||||
void AvatarMixer::aboutToFinish() {
|
||||
DependencyManager::destroy<ResourceManager>();
|
||||
DependencyManager::destroy<ResourceCacheSharedItems>();
|
||||
|
|
|
@ -34,8 +34,8 @@ public:
|
|||
|
||||
static bool shouldReplicateTo(const Node& from, const Node& to) {
|
||||
return to.getType() == NodeType::DownstreamAvatarMixer &&
|
||||
to.getPublicSocket() != from.getPublicSocket() &&
|
||||
to.getLocalSocket() != from.getLocalSocket();
|
||||
to.getPublicSocket() != from.getPublicSocket() &&
|
||||
to.getLocalSocket() != from.getLocalSocket();
|
||||
}
|
||||
|
||||
public slots:
|
||||
|
@ -80,6 +80,7 @@ private:
|
|||
|
||||
// Attach to entity tree for avatar-priority zone info.
|
||||
EntityTreeHeadlessViewer _entityViewer;
|
||||
bool _dirtyHeroStatus { true }; // Dirty the needs-hero-update
|
||||
|
||||
// FIXME - new throttling - use these values somehow
|
||||
float _trailingMixRatio { 0.0f };
|
||||
|
@ -146,6 +147,12 @@ private:
|
|||
|
||||
AvatarMixerSlavePool _slavePool;
|
||||
SlaveSharedData _slaveSharedData;
|
||||
|
||||
public slots:
|
||||
// Avatar zone possibly changed
|
||||
void entityAdded(EntityItem* entity);
|
||||
void entityRemoved(EntityItem* entity);
|
||||
void entityChange();
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarMixer_h
|
||||
|
|
|
@ -141,22 +141,15 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared
|
|||
_avatar->setHasPriorityWithoutTimestampReset(oldHasPriority);
|
||||
|
||||
auto newPosition = getPosition();
|
||||
if (newPosition != oldPosition) {
|
||||
//#define AVATAR_HERO_TEST_HACK
|
||||
#ifdef AVATAR_HERO_TEST_HACK
|
||||
{
|
||||
const static QString heroKey { "HERO" };
|
||||
_avatar->setPriorityAvatar(_avatar->getDisplayName().contains(heroKey));
|
||||
}
|
||||
#else
|
||||
if (newPosition != oldPosition || _avatar->getNeedsHeroCheck()) {
|
||||
EntityTree& entityTree = *slaveSharedData.entityTree;
|
||||
FindPriorityZone findPriorityZone { newPosition, false } ;
|
||||
entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone);
|
||||
_avatar->setHasPriority(findPriorityZone.isInPriorityZone);
|
||||
//if (findPriorityZone.isInPriorityZone) {
|
||||
// qCWarning(avatars) << "Avatar" << _avatar->getSessionDisplayName() << "in hero zone";
|
||||
//}
|
||||
#endif
|
||||
_avatar->setNeedsHeroCheck(false);
|
||||
if (findPriorityZone.isInPriorityZone) {
|
||||
qCWarning(avatars) << "Avatar" << _avatar->getSessionDisplayName() << "in hero zone";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -43,12 +43,14 @@ void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) {
|
|||
|
||||
void AvatarMixerSlave::configureBroadcast(ConstIter begin, ConstIter end,
|
||||
p_high_resolution_clock::time_point lastFrameTimestamp,
|
||||
float maxKbpsPerNode, float throttlingRatio) {
|
||||
float maxKbpsPerNode, float throttlingRatio,
|
||||
float priorityReservedFraction) {
|
||||
_begin = begin;
|
||||
_end = end;
|
||||
_lastFrameTimestamp = lastFrameTimestamp;
|
||||
_maxKbpsPerNode = maxKbpsPerNode;
|
||||
_throttlingRatio = throttlingRatio;
|
||||
_avatarHeroFraction = priorityReservedFraction;
|
||||
}
|
||||
|
||||
void AvatarMixerSlave::harvestStats(AvatarMixerSlaveStats& stats) {
|
||||
|
@ -308,7 +310,6 @@ namespace {
|
|||
} // Close anonymous namespace.
|
||||
|
||||
void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) {
|
||||
const float AVATAR_HERO_FRACTION { 0.4f };
|
||||
const Node* destinationNode = node.data();
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
@ -343,7 +344,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|
||||
// max number of avatarBytes per frame (13 900, typical)
|
||||
const int maxAvatarBytesPerFrame = int(_maxKbpsPerNode * BYTES_PER_KILOBIT / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND);
|
||||
const int maxHeroBytesPerFrame = int(maxAvatarBytesPerFrame * AVATAR_HERO_FRACTION); // 5555, typical
|
||||
const int maxHeroBytesPerFrame = int(maxAvatarBytesPerFrame * _avatarHeroFraction); // 5555, typical
|
||||
|
||||
// keep track of the number of other avatars held back in this frame
|
||||
int numAvatarsHeldBack = 0;
|
||||
|
|
|
@ -110,7 +110,8 @@ public:
|
|||
void configure(ConstIter begin, ConstIter end);
|
||||
void configureBroadcast(ConstIter begin, ConstIter end,
|
||||
p_high_resolution_clock::time_point lastFrameTimestamp,
|
||||
float maxKbpsPerNode, float throttlingRatio);
|
||||
float maxKbpsPerNode, float throttlingRatio,
|
||||
float priorityReservedFraction);
|
||||
|
||||
void processIncomingPackets(const SharedNodePointer& node);
|
||||
void broadcastAvatarData(const SharedNodePointer& node);
|
||||
|
@ -140,6 +141,7 @@ private:
|
|||
p_high_resolution_clock::time_point _lastFrameTimestamp;
|
||||
float _maxKbpsPerNode { 0.0f };
|
||||
float _throttlingRatio { 0.0f };
|
||||
float _avatarHeroFraction { 0.4f };
|
||||
|
||||
AvatarMixerSlaveStats _stats;
|
||||
SlaveSharedData* _sharedData;
|
||||
|
|
|
@ -76,7 +76,8 @@ void AvatarMixerSlavePool::broadcastAvatarData(ConstIter begin, ConstIter end,
|
|||
float maxKbpsPerNode, float throttlingRatio) {
|
||||
_function = &AvatarMixerSlave::broadcastAvatarData;
|
||||
_configure = [=](AvatarMixerSlave& slave) {
|
||||
slave.configureBroadcast(begin, end, lastFrameTimestamp, maxKbpsPerNode, throttlingRatio);
|
||||
slave.configureBroadcast(begin, end, lastFrameTimestamp, maxKbpsPerNode, throttlingRatio,
|
||||
_priorityReservedFraction);
|
||||
};
|
||||
run(begin, end);
|
||||
}
|
||||
|
|
|
@ -73,7 +73,10 @@ public:
|
|||
void each(std::function<void(AvatarMixerSlave& slave)> functor);
|
||||
|
||||
void setNumThreads(int numThreads);
|
||||
int numThreads() { return _numThreads; }
|
||||
int numThreads() const { return _numThreads; }
|
||||
|
||||
void setPriorityReservedFraction(float fraction) { _priorityReservedFraction = fraction; }
|
||||
float getPriorityReservedFraction() const { return _priorityReservedFraction; }
|
||||
|
||||
private:
|
||||
void run(ConstIter begin, ConstIter end);
|
||||
|
@ -91,7 +94,11 @@ private:
|
|||
ConditionVariable _poolCondition;
|
||||
void (AvatarMixerSlave::*_function)(const SharedNodePointer& node);
|
||||
std::function<void(AvatarMixerSlave&)> _configure;
|
||||
|
||||
// Set from Domain Settings:
|
||||
float _priorityReservedFraction { 0.4f };
|
||||
int _numThreads { 0 };
|
||||
|
||||
int _numStarted { 0 }; // guarded by _mutex
|
||||
int _numFinished { 0 }; // guarded by _mutex
|
||||
int _numStopped { 0 }; // guarded by _mutex
|
||||
|
|
|
@ -19,8 +19,12 @@
|
|||
|
||||
class MixerAvatar : public AvatarData {
|
||||
public:
|
||||
bool getNeedsHeroCheck() const { return _needsHeroCheck; }
|
||||
void setNeedsHeroCheck(bool needsHeroCheck = true)
|
||||
{ _needsHeroCheck = needsHeroCheck; }
|
||||
|
||||
private:
|
||||
bool _needsHeroCheck { false };
|
||||
};
|
||||
|
||||
using MixerAvatarSharedPointer = std::shared_ptr<MixerAvatar>;
|
||||
|
|
|
@ -1310,6 +1310,15 @@
|
|||
"placeholder": "50",
|
||||
"default": "50",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "priority_fraction",
|
||||
"type": "double",
|
||||
"label": "Hero Bandwidth",
|
||||
"help": "Fraction of downstream bandwidth reserved for avatars in 'Hero' zones",
|
||||
"placeholder": "0.40",
|
||||
"default": "0.40",
|
||||
"advanced": true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue