mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-10 02:44:34 +02:00
send traits in bulk to avatar mixer client
This commit is contained in:
parent
f23a036f4a
commit
26a1f03314
8 changed files with 188 additions and 11 deletions
|
@ -17,7 +17,8 @@
|
|||
#include <NodeList.h>
|
||||
|
||||
AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) :
|
||||
NodeData(nodeID)
|
||||
NodeData(nodeID),
|
||||
_receivedSimpleTraitVersions(AvatarTraits::SimpleTraitTypes.size())
|
||||
{
|
||||
// in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID
|
||||
_avatar->setID(nodeID);
|
||||
|
@ -92,7 +93,41 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) {
|
|||
}
|
||||
|
||||
void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) {
|
||||
qDebug() << "Pulling a traits message of" << message.getSize();
|
||||
// pull the trait version from the message
|
||||
AvatarTraits::TraitVersion packetTraitVersion;
|
||||
message.readPrimitive(&packetTraitVersion);
|
||||
|
||||
bool anyTraitsChanged = false;
|
||||
|
||||
while (message.getBytesLeftToRead() > 0) {
|
||||
// for each trait in the packet, apply it if the trait version is newer than what we have
|
||||
|
||||
AvatarTraits::TraitType traitType;
|
||||
message.readPrimitive(&traitType);
|
||||
|
||||
AvatarTraits::TraitWireSize traitSize;
|
||||
message.readPrimitive(&traitSize);
|
||||
|
||||
if (packetTraitVersion > _receivedSimpleTraitVersions[traitType]) {
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
// get the URL from the binary data
|
||||
auto skeletonModelURL = QUrl::fromEncoded(message.read(traitSize));
|
||||
_avatar->setSkeletonModelURL(skeletonModelURL);
|
||||
|
||||
qDebug() << "Set skeleton URL to" << skeletonModelURL << "for trait packet version" << packetTraitVersion;
|
||||
|
||||
_receivedSimpleTraitVersions[traitType] = packetTraitVersion;
|
||||
|
||||
anyTraitsChanged = true;
|
||||
}
|
||||
} else {
|
||||
message.seek(message.getPosition() + traitSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (anyTraitsChanged) {
|
||||
_lastReceivedTraitsChange = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t AvatarMixerClientData::getLastBroadcastTime(const QUuid& nodeUUID) const {
|
||||
|
@ -172,3 +207,39 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
|
|||
jsonObject["recent_other_av_in_view"] = _recentOtherAvatarsInView;
|
||||
jsonObject["recent_other_av_out_of_view"] = _recentOtherAvatarsOutOfView;
|
||||
}
|
||||
|
||||
AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar) const {
|
||||
auto it = _lastSentTraitsTimestamps.find(otherAvatar);
|
||||
|
||||
if (it != _lastSentTraitsTimestamps.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return TraitsCheckTimestamp();
|
||||
}
|
||||
}
|
||||
|
||||
AvatarTraits::TraitVersion AvatarMixerClientData::getLastSentSimpleTraitVersion(Node::LocalID otherAvatar,
|
||||
AvatarTraits::TraitType traitType) const {
|
||||
auto it = _sentSimpleTraitVersions.find(otherAvatar);
|
||||
|
||||
if (it != _sentSimpleTraitVersions.end()) {
|
||||
return it->second[traitType];
|
||||
}
|
||||
|
||||
return AvatarTraits::DEFAULT_TRAIT_VERSION;
|
||||
}
|
||||
|
||||
void AvatarMixerClientData::setLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion traitVersion) {
|
||||
|
||||
auto it = _sentSimpleTraitVersions.find(otherAvatar);
|
||||
|
||||
if (it == _sentSimpleTraitVersions.end()) {
|
||||
auto pair = _sentSimpleTraitVersions.insert({
|
||||
otherAvatar, { AvatarTraits::TotalTraitTypes, AvatarTraits::DEFAULT_TRAIT_VERSION }
|
||||
});
|
||||
|
||||
it = pair.first;
|
||||
}
|
||||
|
||||
it->second[traitType] = traitVersion;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <QtCore/QUrl>
|
||||
|
||||
#include <AvatarData.h>
|
||||
#include <AvatarTraits.h>
|
||||
#include <NodeData.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
|
@ -122,6 +123,20 @@ public:
|
|||
|
||||
void processSetTraitsMessage(ReceivedMessage& message);
|
||||
|
||||
using TraitsCheckTimestamp = std::chrono::steady_clock::time_point;
|
||||
|
||||
TraitsCheckTimestamp getLastReceivedTraitsChange() const { return _lastReceivedTraitsChange; }
|
||||
AvatarTraits::TraitVersion getLastReceivedSimpleTraitVersion(AvatarTraits::TraitType traitType) const
|
||||
{ return _receivedSimpleTraitVersions[traitType]; }
|
||||
|
||||
TraitsCheckTimestamp getLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar) const;
|
||||
void setLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar, TraitsCheckTimestamp sendPoint)
|
||||
{ _lastSentTraitsTimestamps[otherAvatar] = sendPoint; }
|
||||
|
||||
AvatarTraits::TraitVersion getLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType) const;
|
||||
void setLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType,
|
||||
AvatarTraits::TraitVersion traitVersion);
|
||||
|
||||
private:
|
||||
struct PacketQueue : public std::queue<QSharedPointer<ReceivedMessage>> {
|
||||
QWeakPointer<Node> node;
|
||||
|
@ -158,6 +173,12 @@ private:
|
|||
int _recentOtherAvatarsOutOfView { 0 };
|
||||
QString _baseDisplayName{}; // The santized key used in determinging unique sessionDisplayName, so that we can remove from dictionary.
|
||||
bool _requestsDomainListData { false };
|
||||
|
||||
AvatarTraits::SimpleTraitVersions _receivedSimpleTraitVersions;
|
||||
TraitsCheckTimestamp _lastReceivedTraitsChange;
|
||||
|
||||
std::unordered_map<Node::LocalID, TraitsCheckTimestamp> _lastSentTraitsTimestamps;
|
||||
std::unordered_map<Node::LocalID, AvatarTraits::SimpleTraitVersions> _sentSimpleTraitVersions;
|
||||
};
|
||||
|
||||
#endif // hifi_AvatarMixerClientData_h
|
||||
|
|
|
@ -79,6 +79,61 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData,
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData,
|
||||
const AvatarMixerClientData* sendingNodeData,
|
||||
NLPacketList& traitsPacketList) {
|
||||
|
||||
auto otherNodeLocalID = sendingNodeData->getNodeLocalID();
|
||||
|
||||
// Perform a simple check with two server clock time points
|
||||
// to see if there is any new traits data for this avatar that we need to send
|
||||
auto timeOfLastTraitsSent = listeningNodeData->getLastOtherAvatarTraitsSendPoint(otherNodeLocalID);
|
||||
auto timeOfLastTraitsChange = sendingNodeData->getLastReceivedTraitsChange();
|
||||
|
||||
if (timeOfLastTraitsChange > timeOfLastTraitsSent) {
|
||||
// there is definitely new traits data to send
|
||||
|
||||
// add the avatar ID to mark the beginning of traits for this avatar
|
||||
traitsPacketList.write(sendingNodeData->getNodeID().toRfc4122());
|
||||
|
||||
auto sendingAvatar = sendingNodeData->getAvatarSharedPointer();
|
||||
|
||||
// compare trait versions so we can see what exactly needs to go out
|
||||
for (int i = 0; i < AvatarTraits::TotalTraitTypes; ++i) {
|
||||
AvatarTraits::TraitType traitType = static_cast<AvatarTraits::TraitType>(i);
|
||||
|
||||
auto lastSentVersion = listeningNodeData->getLastSentSimpleTraitVersion(otherNodeLocalID, traitType);
|
||||
auto lastReceivedVersion = sendingNodeData->getLastReceivedSimpleTraitVersion(traitType);
|
||||
|
||||
if (lastReceivedVersion > lastSentVersion) {
|
||||
// there is an update to this trait, add it to the traits packet
|
||||
|
||||
// write the trait type and the trait version
|
||||
traitsPacketList.writePrimitive(traitType);
|
||||
traitsPacketList.writePrimitive(lastReceivedVersion);
|
||||
|
||||
// update the last sent version since we're adding this to the packet
|
||||
listeningNodeData->setLastSentSimpleTraitVersion(otherNodeLocalID, traitType, lastReceivedVersion);
|
||||
|
||||
if (traitType == AvatarTraits::SkeletonModelURL) {
|
||||
// get an encoded version of the URL, write its size and then the data itself
|
||||
auto encodedSkeletonURL = sendingAvatar->getSkeletonModelURL().toEncoded();
|
||||
|
||||
traitsPacketList.writePrimitive(uint16_t(encodedSkeletonURL.size()));
|
||||
traitsPacketList.write(encodedSkeletonURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write a null trait type to mark the end of trait data for this avatar
|
||||
traitsPacketList.writePrimitive(AvatarTraits::NullTrait);
|
||||
|
||||
// since we send all traits for this other avatar, update the time of last traits sent
|
||||
// to match the time of last traits change
|
||||
listeningNodeData->setLastOtherAvatarTraitsSendPoint(otherNodeLocalID, timeOfLastTraitsChange);
|
||||
}
|
||||
}
|
||||
|
||||
int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode) {
|
||||
if (AvatarMixer::shouldReplicateTo(agentNode, destinationNode)) {
|
||||
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(true);
|
||||
|
@ -326,6 +381,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
// loop through our sorted avatars and allocate our bandwidth to them accordingly
|
||||
|
||||
int remainingAvatars = (int)sortedAvatars.size();
|
||||
auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true);
|
||||
while (!sortedAvatars.empty()) {
|
||||
const auto avatarData = sortedAvatars.top().getAvatar();
|
||||
sortedAvatars.pop();
|
||||
|
@ -392,11 +448,12 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|
||||
quint64 start = usecTimestampNow();
|
||||
QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther,
|
||||
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther);
|
||||
hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition,
|
||||
&lastSentJointsForOther);
|
||||
quint64 end = usecTimestampNow();
|
||||
_stats.toByteArrayElapsedTime += (end - start);
|
||||
|
||||
auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID;
|
||||
static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID;
|
||||
if (bytes.size() > maxAvatarDataBytes) {
|
||||
qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID()
|
||||
<< "resulted in very large buffer of" << bytes.size() << "bytes - dropping facial data";
|
||||
|
@ -445,6 +502,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
|
||||
quint64 endAvatarDataPacking = usecTimestampNow();
|
||||
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
|
||||
|
||||
// use helper to add any changed traits to our packet list
|
||||
addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList);
|
||||
}
|
||||
|
||||
quint64 startPacketSending = usecTimestampNow();
|
||||
|
@ -461,6 +521,14 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
|
|||
// record the bytes sent for other avatar data in the AvatarMixerClientData
|
||||
nodeData->recordSentAvatarData(numAvatarDataBytes);
|
||||
|
||||
// close the current traits packet list
|
||||
traitsPacketList->closeCurrentPacket();
|
||||
|
||||
if (traitsPacketList->getNumPackets() >= 1) {
|
||||
// send the traits packet list
|
||||
nodeList->sendPacketList(std::move(traitsPacketList), *node);
|
||||
}
|
||||
|
||||
// record the number of avatars held back this frame
|
||||
nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack);
|
||||
nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames);
|
||||
|
|
|
@ -99,6 +99,10 @@ private:
|
|||
int sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode);
|
||||
int sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode);
|
||||
|
||||
void addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData,
|
||||
const AvatarMixerClientData* sendingNodeData,
|
||||
NLPacketList& traitsPacketList);
|
||||
|
||||
void broadcastAvatarDataToAgent(const SharedNodePointer& node);
|
||||
void broadcastAvatarDataToDownstreamMixer(const SharedNodePointer& node);
|
||||
|
||||
|
|
|
@ -13,15 +13,25 @@
|
|||
#define hifi_AvatarTraits_h
|
||||
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace AvatarTraits {
|
||||
enum Trait : uint8_t {
|
||||
enum TraitType : int8_t {
|
||||
NullTrait = -1,
|
||||
SkeletonModelURL,
|
||||
TotalTraits
|
||||
TotalTraitTypes
|
||||
};
|
||||
|
||||
using TraitTypeSet = std::set<TraitType>;
|
||||
const TraitTypeSet SimpleTraitTypes = { SkeletonModelURL };
|
||||
|
||||
using TraitVersion = uint32_t;
|
||||
const TraitVersion DEFAULT_TRAIT_VERSION = 0;
|
||||
|
||||
using TraitWireSize = uint16_t;
|
||||
|
||||
using SimpleTraitVersions = std::vector<TraitVersion>;
|
||||
}
|
||||
|
||||
#endif // hifi_AvatarTraits_h
|
||||
|
|
|
@ -68,9 +68,11 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() {
|
|||
|
||||
auto encodedSkeletonURL = _owningAvatar->getSkeletonModelURL().toEncoded();
|
||||
|
||||
uint16_t encodedURLSize = encodedSkeletonURL.size();
|
||||
AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size();
|
||||
traitsPacketList->writePrimitive(encodedURLSize);
|
||||
|
||||
qDebug() << "Sending trait of size" << encodedURLSize;
|
||||
|
||||
traitsPacketList->write(encodedSkeletonURL);
|
||||
|
||||
traitsPacketList->endSegment();
|
||||
|
|
|
@ -25,16 +25,16 @@ public:
|
|||
void sendChangedTraitsToMixer();
|
||||
|
||||
bool hasChangedTraits() { return _changedTraits.size(); }
|
||||
void markTraitChanged(AvatarTraits::Trait changedTrait) { _changedTraits.insert(changedTrait); }
|
||||
void markTraitChanged(AvatarTraits::TraitType changedTrait) { _changedTraits.insert(changedTrait); }
|
||||
|
||||
bool hasTraitChanged(AvatarTraits::Trait checkTrait) { return _changedTraits.count(checkTrait) > 0; }
|
||||
bool hasTraitChanged(AvatarTraits::TraitType checkTrait) { return _changedTraits.count(checkTrait) > 0; }
|
||||
|
||||
void resetForNewMixer();
|
||||
|
||||
private:
|
||||
AvatarData* _owningAvatar;
|
||||
|
||||
std::set<AvatarTraits::Trait> _changedTraits;
|
||||
AvatarTraits::TraitTypeSet _changedTraits;
|
||||
AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION };
|
||||
bool _performInitialSend { false };
|
||||
};
|
||||
|
|
|
@ -133,7 +133,8 @@ public:
|
|||
|
||||
EntityClone,
|
||||
EntityQueryInitialResultsComplete,
|
||||
|
||||
BulkAvatarTraits,
|
||||
|
||||
NUM_PACKET_TYPE
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue