From e8b9594758215f74befd3491c5e5a9349ccbf562 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 09:39:43 -0700 Subject: [PATCH 1/6] spread out billboard and identity fallbacks --- assignment-client/src/avatars/AvatarMixer.cpp | 122 +++--------------- .../src/avatars/AvatarMixerClientData.cpp | 4 +- .../src/avatars/AvatarMixerClientData.h | 12 -- 3 files changed, 22 insertions(+), 116 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 0ec7c3e15e..34544af8ab 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -46,6 +46,8 @@ void attachAvatarDataToNode(Node* newNode) { } } +const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f; + // NOTE: some additional optimizations to consider. // 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present // if the avatar is not in view or in the keyhole. @@ -99,6 +101,23 @@ void AvatarMixer::broadcastAvatarData() { // copy the avatar into the mixedAvatarByteArray packet mixedAvatarByteArray.append(avatarByteArray); + + if (randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY) { + QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); + billboardPacket.append(otherNode->getUUID().toRfc4122()); + billboardPacket.append(otherNodeData->getAvatar().getBillboard()); + nodeList->writeDatagram(billboardPacket, node); + } + + if (randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY) { + QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); + + QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); + individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122()); + identityPacket.append(individualData); + + nodeList->writeDatagram(identityPacket, node); + } } } } @@ -108,66 +127,6 @@ void AvatarMixer::broadcastAvatarData() { } } -void broadcastIdentityPacket() { - - NodeList* nodeList = NodeList::getInstance(); - - QByteArray avatarIdentityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); - int numPacketHeaderBytes = avatarIdentityPacket.size(); - - foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { - if (node->getLinkedData() && node->getType() == NodeType::Agent) { - - AvatarMixerClientData* nodeData = reinterpret_cast(node->getLinkedData()); - AvatarData& avatar = nodeData->getAvatar(); - QByteArray individualData = avatar.identityByteArray(); - individualData.replace(0, NUM_BYTES_RFC4122_UUID, node->getUUID().toRfc4122()); - - if (avatarIdentityPacket.size() + individualData.size() > MAX_PACKET_SIZE) { - // we've hit MTU, send out the current packet before appending - nodeList->broadcastToNodes(avatarIdentityPacket, NodeSet() << NodeType::Agent); - avatarIdentityPacket.resize(numPacketHeaderBytes); - } - - // append the individual data to the current the avatarIdentityPacket - avatarIdentityPacket.append(individualData); - - // re-set the bool in AvatarMixerClientData so a change between key frames gets sent out - nodeData->setHasSentIdentityBetweenKeyFrames(false); - } - } - - // send out the final packet - if (avatarIdentityPacket.size() > numPacketHeaderBytes) { - nodeList->broadcastToNodes(avatarIdentityPacket, NodeSet() << NodeType::Agent); - } -} - -void broadcastBillboardPacket(const SharedNodePointer& sendingNode) { - AvatarMixerClientData* nodeData = static_cast(sendingNode->getLinkedData()); - AvatarData& avatar = nodeData->getAvatar(); - QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); - packet.append(sendingNode->getUUID().toRfc4122()); - packet.append(avatar.getBillboard()); - - NodeList* nodeList = NodeList::getInstance(); - foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { - if (node->getType() == NodeType::Agent && node != sendingNode) { - nodeList->writeDatagram(packet, node); - } - } -} - -void broadcastBillboardPackets() { - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - if (node->getLinkedData() && node->getType() == NodeType::Agent) { - AvatarMixerClientData* nodeData = static_cast(node->getLinkedData()); - broadcastBillboardPacket(node); - nodeData->setHasSentBillboardBetweenKeyFrames(false); - } - } -} - void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { if (killedNode->getType() == NodeType::Agent && killedNode->getLinkedData()) { @@ -202,19 +161,7 @@ void AvatarMixer::readPendingDatagrams() { if (avatarNode && avatarNode->getLinkedData()) { AvatarMixerClientData* nodeData = reinterpret_cast(avatarNode->getLinkedData()); AvatarData& avatar = nodeData->getAvatar(); - if (avatar.hasIdentityChangedAfterParsing(receivedPacket) - && !nodeData->hasSentIdentityBetweenKeyFrames()) { - // this avatar changed their identity in some way and we haven't sent a packet in this keyframe - QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); - - QByteArray individualByteArray = avatar.identityByteArray(); - individualByteArray.replace(0, NUM_BYTES_RFC4122_UUID, avatarNode->getUUID().toRfc4122()); - - identityPacket.append(individualByteArray); - - nodeData->setHasSentIdentityBetweenKeyFrames(true); - nodeList->broadcastToNodes(identityPacket, NodeSet() << NodeType::Agent); - } + avatar.hasIdentityChangedAfterParsing(receivedPacket); } break; } @@ -226,12 +173,7 @@ void AvatarMixer::readPendingDatagrams() { if (avatarNode && avatarNode->getLinkedData()) { AvatarMixerClientData* nodeData = static_cast(avatarNode->getLinkedData()); AvatarData& avatar = nodeData->getAvatar(); - if (avatar.hasBillboardChangedAfterParsing(receivedPacket) - && !nodeData->hasSentBillboardBetweenKeyFrames()) { - // this avatar changed their billboard and we haven't sent a packet in this keyframe - broadcastBillboardPacket(avatarNode); - nodeData->setHasSentBillboardBetweenKeyFrames(true); - } + avatar.hasBillboardChangedAfterParsing(receivedPacket); } break; } @@ -261,9 +203,6 @@ void AvatarMixer::sendStatsPacket() { _numStatFrames = 0; } -const qint64 AVATAR_IDENTITY_KEYFRAME_MSECS = 5000; -const qint64 AVATAR_BILLBOARD_KEYFRAME_MSECS = 5000; - void AvatarMixer::run() { ThreadedAssignment::commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer); @@ -277,12 +216,6 @@ void AvatarMixer::run() { gettimeofday(&startTime, NULL); - QElapsedTimer identityTimer; - identityTimer.start(); - - QElapsedTimer billboardTimer; - billboardTimer.start(); - int usecToSleep = AVATAR_DATA_SEND_INTERVAL_USECS; const int TRAILING_AVERAGE_FRAMES = 100; @@ -338,19 +271,6 @@ void AvatarMixer::run() { broadcastAvatarData(); - if (identityTimer.elapsed() >= AVATAR_IDENTITY_KEYFRAME_MSECS) { - // it's time to broadcast the keyframe identity packets - broadcastIdentityPacket(); - - // restart the timer so we do it again in AVATAR_IDENTITY_KEYFRAME_MSECS - identityTimer.restart(); - } - - if (billboardTimer.elapsed() >= AVATAR_BILLBOARD_KEYFRAME_MSECS) { - broadcastBillboardPackets(); - billboardTimer.restart(); - } - QCoreApplication::processEvents(); if (_isFinished) { diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 90088173a9..5847f72ffb 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -9,9 +9,7 @@ #include "AvatarMixerClientData.h" AvatarMixerClientData::AvatarMixerClientData() : - NodeData(), - _hasSentIdentityBetweenKeyFrames(false), - _hasSentBillboardBetweenKeyFrames(false) + NodeData() { } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 854e8172d3..9accc70291 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -20,21 +20,9 @@ public: AvatarMixerClientData(); int parseData(const QByteArray& packet); - - bool hasSentIdentityBetweenKeyFrames() const { return _hasSentIdentityBetweenKeyFrames; } - void setHasSentIdentityBetweenKeyFrames(bool hasSentIdentityBetweenKeyFrames) - { _hasSentIdentityBetweenKeyFrames = hasSentIdentityBetweenKeyFrames; } - - bool hasSentBillboardBetweenKeyFrames() const { return _hasSentBillboardBetweenKeyFrames; } - void setHasSentBillboardBetweenKeyFrames(bool hasSentBillboardBetweenKeyFrames) - { _hasSentBillboardBetweenKeyFrames = hasSentBillboardBetweenKeyFrames; } - AvatarData& getAvatar() { return _avatar; } private: - - bool _hasSentIdentityBetweenKeyFrames; - bool _hasSentBillboardBetweenKeyFrames; AvatarData _avatar; }; From e6b83ac52dda1a6a39951877c369f3ac27019ba4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 10:34:38 -0700 Subject: [PATCH 2/6] send mesh and billboard on receiver's first connect --- assignment-client/src/avatars/AvatarMixer.cpp | 6 ++++-- assignment-client/src/avatars/AvatarMixerClientData.cpp | 9 ++++++++- assignment-client/src/avatars/AvatarMixerClientData.h | 5 ++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 34544af8ab..4d09228dd0 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -102,14 +102,16 @@ void AvatarMixer::broadcastAvatarData() { // copy the avatar into the mixedAvatarByteArray packet mixedAvatarByteArray.append(avatarByteArray); - if (randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY) { + bool forceSend = !myData->checkAndSetHasReceivedFirstPackets(); + + if (randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY || forceSend) { QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); billboardPacket.append(otherNode->getUUID().toRfc4122()); billboardPacket.append(otherNodeData->getAvatar().getBillboard()); nodeList->writeDatagram(billboardPacket, node); } - if (randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY) { + if (randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY || forceSend) { QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 5847f72ffb..84db17ecf4 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -9,7 +9,8 @@ #include "AvatarMixerClientData.h" AvatarMixerClientData::AvatarMixerClientData() : - NodeData() + NodeData(), + _hasReceivedFirstPackets(false) { } @@ -19,3 +20,9 @@ int AvatarMixerClientData::parseData(const QByteArray& packet) { int offset = numBytesForPacketHeader(packet); return _avatar.parseDataAtOffset(packet, offset); } + +bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() { + bool oldValue = _hasReceivedFirstPackets; + _hasReceivedFirstPackets = true; + return oldValue; +} diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 9accc70291..5cf61659a7 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -21,9 +21,12 @@ public: int parseData(const QByteArray& packet); AvatarData& getAvatar() { return _avatar; } - + + bool checkAndSetHasReceivedFirstPackets(); + private: AvatarData _avatar; + bool _hasReceivedFirstPackets; }; #endif /* defined(__hifi__AvatarMixerClientData__) */ From f763859fd0f8835b9587c0d554be5384f9228bf9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 10:44:52 -0700 Subject: [PATCH 3/6] don't attempt to render avatars until they are initialized --- interface/src/avatar/AvatarManager.cpp | 76 +++++++++++++++----------- interface/src/avatar/AvatarManager.h | 6 +- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 9147a08dbd..bc1f826606 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -48,15 +48,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { Avatar* avatar = static_cast(avatarIterator.value().data()); - if (avatar == static_cast(_myAvatar.data())) { + if (avatar == static_cast(_myAvatar.data()) || !avatar->isInitialized()) { // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. - //updateMyAvatar(deltaTime); + // DO NOT update uninitialized Avatars ++avatarIterator; continue; } - if (!avatar->isInitialized()) { - avatar->init(); - } if (avatar->getOwningAvatarMixer()) { // this avatar's mixer is still around, go ahead and simulate it avatar->simulate(deltaTime); @@ -104,12 +101,14 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { while (fadingIterator != _avatarFades.end()) { Avatar* avatar = static_cast(fadingIterator->data()); - avatar->setTargetScale(avatar->getScale() * SHRINK_RATE); - if (avatar->getTargetScale() < MIN_FADE_SCALE) { - fadingIterator = _avatarFades.erase(fadingIterator); - } else { - avatar->simulate(deltaTime); - ++fadingIterator; + if (avatar->isInitialized()) { + avatar->setTargetScale(avatar->getScale() * SHRINK_RATE); + if (avatar->getTargetScale() < MIN_FADE_SCALE) { + fadingIterator = _avatarFades.erase(fadingIterator); + } else { + avatar->simulate(deltaTime); + ++fadingIterator; + } } } } @@ -120,22 +119,40 @@ void AvatarManager::renderAvatarFades(const glm::vec3& cameraPosition, Avatar::R foreach(const AvatarSharedPointer& fadingAvatar, _avatarFades) { Avatar* avatar = static_cast(fadingAvatar.data()); - if (avatar != static_cast(_myAvatar.data())) { + if (avatar != static_cast(_myAvatar.data()) && avatar->isInitialized()) { avatar->render(cameraPosition, renderMode); } } } +AvatarSharedPointer AvatarManager::matchingOrNewAvatar(const QUuid& nodeUUID, const QWeakPointer& mixerWeakPointer) { + AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID); + + if (!matchingAvatar) { + // construct a new Avatar for this node + Avatar* avatar = new Avatar(); + avatar->setOwningAvatarMixer(mixerWeakPointer); + + // insert the new avatar into our hash + matchingAvatar = AvatarSharedPointer(avatar); + _avatarHash.insert(nodeUUID, matchingAvatar); + + qDebug() << "Adding avatar with UUID" << nodeUUID << "to AvatarManager hash."; + } + + return matchingAvatar; +} + void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer& mixerWeakPointer) { switch (packetTypeForPacket(datagram)) { case PacketTypeBulkAvatarData: processAvatarDataPacket(datagram, mixerWeakPointer); break; case PacketTypeAvatarIdentity: - processAvatarIdentityPacket(datagram); + processAvatarIdentityPacket(datagram, mixerWeakPointer); break; case PacketTypeAvatarBillboard: - processAvatarBillboardPacket(datagram); + processAvatarBillboardPacket(datagram, mixerWeakPointer); break; case PacketTypeKillAvatar: processKillAvatar(datagram); @@ -154,26 +171,21 @@ void AvatarManager::processAvatarDataPacket(const QByteArray &datagram, const QW QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID)); bytesRead += NUM_BYTES_RFC4122_UUID; - AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID); - - if (!matchingAvatar) { - // construct a new Avatar for this node - Avatar* avatar = new Avatar(); - avatar->setOwningAvatarMixer(mixerWeakPointer); - - // insert the new avatar into our hash - matchingAvatar = AvatarSharedPointer(avatar); - _avatarHash.insert(nodeUUID, matchingAvatar); - - qDebug() << "Adding avatar with UUID" << nodeUUID << "to AvatarManager hash."; - } + AvatarSharedPointer matchingAvatarData = matchingOrNewAvatar(nodeUUID, mixerWeakPointer); // have the matching (or new) avatar parse the data from the packet - bytesRead += matchingAvatar->parseDataAtOffset(datagram, bytesRead); + bytesRead += matchingAvatarData->parseDataAtOffset(datagram, bytesRead); + + Avatar* matchingAvatar = reinterpret_cast(matchingAvatarData.data()); + + if (!matchingAvatar->isInitialized()) { + // now that we have AvatarData for this Avatar we are go for init + matchingAvatar->init(); + } } } -void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet) { +void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet, const QWeakPointer& mixerWeakPointer) { // setup a data stream to parse the packet QDataStream identityStream(packet); identityStream.skipRawData(numBytesForPacketHeader(packet)); @@ -187,7 +199,7 @@ void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet) { identityStream >> nodeUUID >> faceMeshURL >> skeletonURL >> displayName; // mesh URL for a UUID, find avatar in our list - AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID); + AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(nodeUUID, mixerWeakPointer); if (matchingAvatar) { Avatar* avatar = static_cast(matchingAvatar.data()); @@ -206,11 +218,11 @@ void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet) { } } -void AvatarManager::processAvatarBillboardPacket(const QByteArray& packet) { +void AvatarManager::processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer& mixerWeakPointer) { int headerSize = numBytesForPacketHeader(packet); QUuid nodeUUID = QUuid::fromRfc4122(QByteArray::fromRawData(packet.constData() + headerSize, NUM_BYTES_RFC4122_UUID)); - AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID); + AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(nodeUUID, mixerWeakPointer); if (matchingAvatar) { Avatar* avatar = static_cast(matchingAvatar.data()); QByteArray billboard = packet.mid(headerSize + NUM_BYTES_RFC4122_UUID); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 06494f309c..bd04dddb78 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -39,9 +39,11 @@ public slots: private: AvatarManager(const AvatarManager& other); + AvatarSharedPointer matchingOrNewAvatar(const QUuid& nodeUUID, const QWeakPointer& mixerWeakPointer); + void processAvatarDataPacket(const QByteArray& packet, const QWeakPointer& mixerWeakPointer); - void processAvatarIdentityPacket(const QByteArray& packet); - void processAvatarBillboardPacket(const QByteArray& packet); + void processAvatarIdentityPacket(const QByteArray& packet, const QWeakPointer& mixerWeakPointer); + void processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer& mixerWeakPointer); void processKillAvatar(const QByteArray& datagram); void simulateAvatarFades(float deltaTime); From 786d9ce404a42c9c2ddf9f173a800ad62fb7ef46 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 10:56:06 -0700 Subject: [PATCH 4/6] force sending of identity and billboard packets changed in last frame --- assignment-client/src/avatars/AvatarMixer.cpp | 33 ++++++++++++++++--- assignment-client/src/avatars/AvatarMixer.h | 2 ++ .../src/avatars/AvatarMixerClientData.cpp | 4 ++- .../src/avatars/AvatarMixerClientData.h | 8 +++++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 4d09228dd0..e14cb5868f 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -11,7 +11,7 @@ // nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms. #include -#include +#include #include #include @@ -31,6 +31,7 @@ const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000; AvatarMixer::AvatarMixer(const QByteArray& packet) : ThreadedAssignment(packet), + _lastFrameTimestamp(QDateTime::currentMSecsSinceEpoch()), _trailingSleepRatio(1.0f), _performanceThrottlingRatio(0.0f), _sumListeners(0), @@ -102,16 +103,27 @@ void AvatarMixer::broadcastAvatarData() { // copy the avatar into the mixedAvatarByteArray packet mixedAvatarByteArray.append(avatarByteArray); + // if the receiving avatar has just connected make sure we send out the mesh and billboard + // for this avatar (assuming they exist) bool forceSend = !myData->checkAndSetHasReceivedFirstPackets(); - if (randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY || forceSend) { + // we will also force a send of billboard or identity packet + // if either has changed in the last frame + + if (otherNodeData->getBillboardChangeTimestamp() > 0 + && (forceSend + || otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp + || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard); billboardPacket.append(otherNode->getUUID().toRfc4122()); billboardPacket.append(otherNodeData->getAvatar().getBillboard()); nodeList->writeDatagram(billboardPacket, node); } - if (randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY || forceSend) { + if (otherNodeData->getIdentityChangeTimestamp() > 0 + && (forceSend + || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp + || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); @@ -127,6 +139,8 @@ void AvatarMixer::broadcastAvatarData() { nodeList->writeDatagram(mixedAvatarByteArray, node); } } + + _lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch(); } void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { @@ -163,7 +177,11 @@ void AvatarMixer::readPendingDatagrams() { if (avatarNode && avatarNode->getLinkedData()) { AvatarMixerClientData* nodeData = reinterpret_cast(avatarNode->getLinkedData()); AvatarData& avatar = nodeData->getAvatar(); - avatar.hasIdentityChangedAfterParsing(receivedPacket); + + // parse the identity packet and update the change timestamp if appropriate + if (avatar.hasIdentityChangedAfterParsing(receivedPacket)) { + nodeData->setIdentityChangeTimestamp(QDateTime::currentMSecsSinceEpoch()); + } } break; } @@ -175,7 +193,12 @@ void AvatarMixer::readPendingDatagrams() { if (avatarNode && avatarNode->getLinkedData()) { AvatarMixerClientData* nodeData = static_cast(avatarNode->getLinkedData()); AvatarData& avatar = nodeData->getAvatar(); - avatar.hasBillboardChangedAfterParsing(receivedPacket); + + // parse the billboard packet and update the change timestamp if appropriate + if (avatar.hasBillboardChangedAfterParsing(receivedPacket)) { + nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch()); + } + } break; } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 4d54b715f8..d4e354f347 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -30,6 +30,8 @@ public slots: private: void broadcastAvatarData(); + quint64 _lastFrameTimestamp; + float _trailingSleepRatio; float _performanceThrottlingRatio; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 84db17ecf4..d1449e956e 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -10,7 +10,9 @@ AvatarMixerClientData::AvatarMixerClientData() : NodeData(), - _hasReceivedFirstPackets(false) + _hasReceivedFirstPackets(false), + _billboardChangeTimestamp(0), + _identityChangeTimestamp(0) { } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 5cf61659a7..bc0a54f06b 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -24,9 +24,17 @@ public: bool checkAndSetHasReceivedFirstPackets(); + quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; } + void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; } + + quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; } + void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; } + private: AvatarData _avatar; bool _hasReceivedFirstPackets; + quint64 _billboardChangeTimestamp; + quint64 _identityChangeTimestamp; }; #endif /* defined(__hifi__AvatarMixerClientData__) */ From 8a796be785562db948ca2f900a3c65c8ea80c456 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 11:10:28 -0700 Subject: [PATCH 5/6] only fade avatars if they have ever been initialized --- interface/src/avatar/AvatarManager.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index bc1f826606..c2ba28ac7b 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -101,14 +101,12 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { while (fadingIterator != _avatarFades.end()) { Avatar* avatar = static_cast(fadingIterator->data()); - if (avatar->isInitialized()) { - avatar->setTargetScale(avatar->getScale() * SHRINK_RATE); - if (avatar->getTargetScale() < MIN_FADE_SCALE) { - fadingIterator = _avatarFades.erase(fadingIterator); - } else { - avatar->simulate(deltaTime); - ++fadingIterator; - } + avatar->setTargetScale(avatar->getScale() * SHRINK_RATE); + if (avatar->getTargetScale() < MIN_FADE_SCALE) { + fadingIterator = _avatarFades.erase(fadingIterator); + } else { + avatar->simulate(deltaTime); + ++fadingIterator; } } } @@ -246,7 +244,9 @@ void AvatarManager::processKillAvatar(const QByteArray& datagram) { AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator) { if (iterator.key() != MY_AVATAR_KEY) { qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash."; - _avatarFades.push_back(iterator.value()); + if (reinterpret_cast(iterator.value().data())->isInitialized()) { + _avatarFades.push_back(iterator.value()); + } return AvatarHashMap::erase(iterator); } else { // never remove _myAvatar from the list From ff788a340b300c585c20fdaf49d519b0bb39f4b8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 25 Mar 2014 11:38:00 -0700 Subject: [PATCH 6/6] add stat for average billboard and identity packets per frame --- assignment-client/src/avatars/AvatarMixer.cpp | 14 +++++++++++++- assignment-client/src/avatars/AvatarMixer.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index e14cb5868f..54f1943930 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -35,7 +35,9 @@ AvatarMixer::AvatarMixer(const QByteArray& packet) : _trailingSleepRatio(1.0f), _performanceThrottlingRatio(0.0f), _sumListeners(0), - _numStatFrames(0) + _numStatFrames(0), + _sumBillboardPackets(0), + _sumIdentityPackets(0) { // make sure we hear about node kills so we can tell the other nodes connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled); @@ -118,12 +120,15 @@ void AvatarMixer::broadcastAvatarData() { billboardPacket.append(otherNode->getUUID().toRfc4122()); billboardPacket.append(otherNodeData->getAvatar().getBillboard()); nodeList->writeDatagram(billboardPacket, node); + + ++_sumBillboardPackets; } if (otherNodeData->getIdentityChangeTimestamp() > 0 && (forceSend || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp || randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) { + QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); QByteArray individualData = otherNodeData->getAvatar().identityByteArray(); @@ -131,6 +136,8 @@ void AvatarMixer::broadcastAvatarData() { identityPacket.append(individualData); nodeList->writeDatagram(identityPacket, node); + + ++_sumIdentityPackets; } } } @@ -219,12 +226,17 @@ void AvatarMixer::sendStatsPacket() { QJsonObject statsObject; statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames; + statsObject["average_billboard_packets_per_frame"] = (float) _sumBillboardPackets / (float) _numStatFrames; + statsObject["average_identity_packets_per_frame"] = (float) _sumIdentityPackets / (float) _numStatFrames; + statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100; statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio; ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); _sumListeners = 0; + _sumBillboardPackets = 0; + _sumIdentityPackets = 0; _numStatFrames = 0; } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index d4e354f347..4171df49af 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -37,6 +37,8 @@ private: int _sumListeners; int _numStatFrames; + int _sumBillboardPackets; + int _sumIdentityPackets; }; #endif /* defined(__hifi__AvatarMixer__) */