avoid duplicate parse of AvatarEntityData entries

This commit is contained in:
Andrew Meadows 2017-02-10 11:13:05 -08:00
parent c551a41e0a
commit d2315c15a1
2 changed files with 57 additions and 8 deletions

View file

@ -231,13 +231,40 @@ void Avatar::updateAvatarEntities() {
QScriptEngine scriptEngine;
entityTree->withWriteLock([&] {
AvatarEntityMap avatarEntities = getAvatarEntityData();
for (auto entityID : avatarEntities.keys()) {
AvatarEntityMap::const_iterator dataItr = avatarEntities.begin();
while (dataItr != avatarEntities.end()) {
// compute hash of data. TODO? cache this?
QByteArray data = dataItr.value();
uint32_t newHash = qHash(data);
// check to see if we recognize this hash and whether it was already successfully processed
QUuid entityID = dataItr.key();
MapOfAvatarEntityDataHashes::iterator stateItr = _avatarEntityDataHashes.find(entityID);
if (stateItr != _avatarEntityDataHashes.end()) {
if (stateItr.value().success) {
if (newHash == stateItr.value().hash) {
// data hasn't changed --> nothing to do
++dataItr;
continue;
}
} else {
// NOTE: if the data was unsuccessful in producing an entity in the past
// we will try again just in case something changed (unlikely).
// Unfortunately constantly trying to build the entity for this data costs
// CPU cycles that we'd rather not spend.
// TODO? put a maximum number of tries on this?
}
} else {
// remember this hash for the future
stateItr = _avatarEntityDataHashes.insert(entityID, AvatarEntityDataHash(newHash));
}
++dataItr;
// see EntityEditPacketSender::queueEditEntityMessage for the other end of this. unpack properties
// and either add or update the entity.
QByteArray jsonByteArray = avatarEntities.value(entityID);
QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(jsonByteArray);
QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(data);
if (!jsonProperties.isObject()) {
qCDebug(interfaceapp) << "got bad avatarEntity json" << QString(jsonByteArray.toHex());
qCDebug(interfaceapp) << "got bad avatarEntity json" << QString(data.toHex());
continue;
}
@ -265,18 +292,22 @@ void Avatar::updateAvatarEntities() {
properties.setScript(noScript);
}
// try to build the entity
EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID));
// NOTE: we don't bother checking whether updateEntity or addEntity succeed here:
// if not we'll try again next time the data is changed. This is to close a performance DOS vector
// whereby invalid entity data is given to the AvatarMixer and constant retries kill performance.
bool success = true;
if (entity) {
if (entityTree->updateEntity(entityID, properties)) {
entity->updateLastEditedFromRemote();
} else {
success = false;
}
} else {
entity = entityTree->addEntity(entityID, properties);
if (!entity) {
success = false;
}
}
stateItr.value().success = success;
}
AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs();
@ -289,6 +320,14 @@ void Avatar::updateAvatarEntities() {
}
}
});
// remove stale data hashes
foreach (auto entityID, recentlyDettachedAvatarEntities) {
MapOfAvatarEntityDataHashes::iterator stateItr = _avatarEntityDataHashes.find(entityID);
if (stateItr != _avatarEntityDataHashes.end()) {
_avatarEntityDataHashes.erase(stateItr);
}
}
}
});

View file

@ -269,6 +269,16 @@ protected:
private:
class AvatarEntityDataHash {
public:
AvatarEntityDataHash(uint32_t h) : hash(h) {};
uint32_t hash { 0 };
bool success { false };
};
using MapOfAvatarEntityDataHashes = QMap<QUuid, AvatarEntityDataHash>;
MapOfAvatarEntityDataHashes _avatarEntityDataHashes;
uint64_t _lastRenderUpdateTime { 0 };
int _leftPointerGeometryID { 0 };
int _rightPointerGeometryID { 0 };