Merge branch 'master' of https://github.com/highfidelity/hifi into avatarMixerLoopImprovements

This commit is contained in:
Brad Hefta-Gaub 2017-02-21 14:18:36 -08:00
commit a526db1779
16 changed files with 292 additions and 28 deletions

View file

@ -17,10 +17,11 @@
#include <ScriptCache.h>
#include <EntityEditFilters.h>
#include "AssignmentParentFinder.h"
#include "EntityNodeData.h"
#include "EntityServer.h"
#include "EntityServerConsts.h"
#include "EntityNodeData.h"
#include "AssignmentParentFinder.h"
#include "EntityTreeSendThread.h"
const char* MODEL_SERVER_NAME = "Entity";
const char* MODEL_SERVER_LOGGING_TARGET_NAME = "entity-server";
@ -77,6 +78,10 @@ OctreePointer EntityServer::createTree() {
return tree;
}
OctreeServer::UniqueSendThread EntityServer::newSendThread(const SharedNodePointer& node) {
return std::unique_ptr<EntityTreeSendThread>(new EntityTreeSendThread(this, node));
}
void EntityServer::beforeRun() {
_pruneDeletedEntitiesTimer = new QTimer();
connect(_pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities()));

View file

@ -67,6 +67,7 @@ public slots:
protected:
virtual OctreePointer createTree() override;
virtual UniqueSendThread newSendThread(const SharedNodePointer& node) override;
private slots:
void handleEntityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);

View file

@ -0,0 +1,132 @@
//
// EntityTreeSendThread.cpp
// assignment-client/src/entities
//
// Created by Stephen Birarda on 2/15/17.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityTreeSendThread.h"
#include <EntityNodeData.h>
#include <EntityTypes.h>
#include "EntityServer.h"
void EntityTreeSendThread::preDistributionProcessing() {
auto node = _node.toStrongRef();
auto nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
if (nodeData) {
auto jsonQuery = nodeData->getJSONParameters();
// check if we have a JSON query with flags
auto flags = jsonQuery[EntityJSONQueryProperties::FLAGS_PROPERTY].toObject();
if (!flags.isEmpty()) {
// check the flags object for specific flags that require special pre-processing
bool includeAncestors = flags[EntityJSONQueryProperties::INCLUDE_ANCESTORS_PROPERTY].toBool();
bool includeDescendants = flags[EntityJSONQueryProperties::INCLUDE_DESCENDANTS_PROPERTY].toBool();
if (includeAncestors || includeDescendants) {
// we need to either include the ancestors, descendants, or both for entities matching the filter
// included in the JSON query
// first reset our flagged extra entities so we start with an empty set
nodeData->resetFlaggedExtraEntities();
auto entityTree = std::static_pointer_cast<EntityTree>(_myServer->getOctree());
bool requiresFullScene = false;
// enumerate the set of entity IDs we know currently match the filter
foreach(const QUuid& entityID, nodeData->getSentFilteredEntities()) {
if (includeAncestors) {
// we need to include ancestors - recurse up to reach them all and add their IDs
// to the set of extra entities to include for this node
entityTree->withReadLock([&]{
auto filteredEntity = entityTree->findEntityByID(entityID);
if (filteredEntity) {
requiresFullScene |= addAncestorsToExtraFlaggedEntities(entityID, *filteredEntity, *nodeData);
}
});
}
if (includeDescendants) {
// we need to include descendants - recurse down to reach them all and add their IDs
// to the set of extra entities to include for this node
entityTree->withReadLock([&]{
auto filteredEntity = entityTree->findEntityByID(entityID);
if (filteredEntity) {
requiresFullScene |= addDescendantsToExtraFlaggedEntities(entityID, *filteredEntity, *nodeData);
}
});
}
}
if (requiresFullScene) {
// for one or more of the entities matching our filter we found new extra entities to include
// because it is possible that one of these entities hasn't changed since our last send
// and therefore would not be recursed to, we need to force a full traversal for this pass
// of the tree to allow it to grab all of the extra entities we're asking it to include
nodeData->setShouldForceFullScene(requiresFullScene);
}
}
}
}
}
bool EntityTreeSendThread::addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID,
EntityItem& entityItem, EntityNodeData& nodeData) {
// check if this entity has a parent that is also an entity
bool success = false;
auto entityParent = entityItem.getParentPointer(success);
if (success && entityParent && entityParent->getNestableType() == NestableType::Entity) {
// we found a parent that is an entity item
// first add it to the extra list of things we need to send
bool parentWasNew = nodeData.insertFlaggedExtraEntity(filteredEntityID, entityParent->getID());
// now recursively call ourselves to get its ancestors added too
auto parentEntityItem = std::static_pointer_cast<EntityItem>(entityParent);
bool ancestorsWereNew = addAncestorsToExtraFlaggedEntities(filteredEntityID, *parentEntityItem, nodeData);
// return boolean if our parent or any of our ancestors were new additions (via insertFlaggedExtraEntity)
return parentWasNew || ancestorsWereNew;
}
// since we didn't have a parent niether of our parents or ancestors could be new additions
return false;
}
bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID,
EntityItem& entityItem, EntityNodeData& nodeData) {
bool hasNewChild = false;
bool hasNewDescendants = false;
// enumerate the immediate children of this entity
foreach (SpatiallyNestablePointer child, entityItem.getChildren()) {
if (child && child->getNestableType() == NestableType::Entity) {
// this is a child that is an entity
// first add it to the extra list of things we need to send
hasNewChild |= nodeData.insertFlaggedExtraEntity(filteredEntityID, child->getID());
// now recursively call ourselves to get its descendants added too
auto childEntityItem = std::static_pointer_cast<EntityItem>(child);
hasNewDescendants |= addDescendantsToExtraFlaggedEntities(filteredEntityID, *childEntityItem, nodeData);
}
}
// return our boolean indicating if we added new children or descendants as extra entities to send
// (via insertFlaggedExtraEntity)
return hasNewChild || hasNewDescendants;
}

View file

@ -0,0 +1,35 @@
//
// EntityTreeSendThread.h
// assignment-client/src/entities
//
// Created by Stephen Birarda on 2/15/17.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_EntityTreeSendThread_h
#define hifi_EntityTreeSendThread_h
#include "../octree/OctreeSendThread.h"
class EntityNodeData;
class EntityItem;
class EntityTreeSendThread : public OctreeSendThread {
public:
EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) : OctreeSendThread(myServer, node) {};
protected:
virtual void preDistributionProcessing() override;
private:
// the following two methods return booleans to indicate if any extra flagged entities were new additions to set
bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
};
#endif // hifi_EntityTreeSendThread_h

View file

@ -309,6 +309,12 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
return 0;
}
if (nodeData->elementBag.isEmpty()) {
// if we're about to do a fresh pass,
// give our pre-distribution processing a chance to do what it needs
preDistributionProcessing();
}
// calculate max number of packets that can be sent during this interval
int clientMaxPacketsPerInterval = std::max(1, (nodeData->getMaxQueryPacketsPerSecond() / INTERVALS_PER_SECOND));
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
@ -316,9 +322,17 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
int truePacketsSent = 0;
int trueBytesSent = 0;
int packetsSentThisInterval = 0;
bool isFullScene = nodeData->haveJSONParametersChanged() ||
(nodeData->getUsesFrustum()
&& ((!viewFrustumChanged && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged()));
bool isFullScene = nodeData->shouldForceFullScene();
if (isFullScene) {
// we're forcing a full scene, clear the force in OctreeQueryNode so we don't force it next time again
nodeData->setShouldForceFullScene(false);
} else {
// we aren't forcing a full scene, check if something else suggests we should
isFullScene = nodeData->haveJSONParametersChanged() ||
(nodeData->getUsesFrustum()
&& ((!viewFrustumChanged && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged()));
}
bool somethingToSend = true; // assume we have something

View file

@ -17,6 +17,8 @@
#include <atomic>
#include <GenericThread.h>
#include <Node.h>
#include <OctreePacketData.h>
class OctreeQueryNode;
class OctreeServer;
@ -49,13 +51,17 @@ protected:
/// Implements generic processing behavior for this thread.
virtual bool process() override;
/// Called before a packetDistributor pass to allow for pre-distribution processing
virtual void preDistributionProcessing() {};
OctreeServer* _myServer { nullptr };
QWeakPointer<Node> _node;
private:
int handlePacketSend(SharedNodePointer node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent, bool dontSuppressDuplicate = false);
int packetDistributor(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
OctreeServer* _myServer { nullptr };
QWeakPointer<Node> _node;
QUuid _nodeUuid;
OctreePacketData _packetData;

View file

@ -872,8 +872,12 @@ void OctreeServer::parsePayload() {
}
}
OctreeServer::UniqueSendThread OctreeServer::newSendThread(const SharedNodePointer& node) {
return std::unique_ptr<OctreeSendThread>(new OctreeSendThread(this, node));
}
OctreeServer::UniqueSendThread OctreeServer::createSendThread(const SharedNodePointer& node) {
auto sendThread = std::unique_ptr<OctreeSendThread>(new OctreeSendThread(this, node));
auto sendThread = newSendThread(node);
// we want to be notified when the thread finishes
connect(sendThread.get(), &GenericThread::finished, this, &OctreeServer::removeSendThread);

View file

@ -158,6 +158,7 @@ protected:
QString getStatusLink();
UniqueSendThread createSendThread(const SharedNodePointer& node);
virtual UniqueSendThread newSendThread(const SharedNodePointer& node);
int _argc;
const char** _argv;

View file

@ -16,6 +16,7 @@
#include <AudioConstants.h>
#include <AudioInjectorManager.h>
#include <ClientServerUtils.h>
#include <EntityNodeData.h>
#include <EntityScriptingInterface.h>
#include <LogHandler.h>
#include <MessagesClient.h>
@ -264,8 +265,14 @@ void EntityScriptServer::run() {
// setup the JSON filter that asks for entities with a non-default serverScripts property
QJsonObject queryJSONParameters;
static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts";
queryJSONParameters[SERVER_SCRIPTS_PROPERTY] = EntityQueryFilterSymbol::NonDefault;
queryJSONParameters[EntityJSONQueryProperties::SERVER_SCRIPTS_PROPERTY] = EntityQueryFilterSymbol::NonDefault;
QJsonObject queryFlags;
queryFlags[EntityJSONQueryProperties::INCLUDE_ANCESTORS_PROPERTY] = true;
queryFlags[EntityJSONQueryProperties::INCLUDE_DESCENDANTS_PROPERTY] = true;
queryJSONParameters[EntityJSONQueryProperties::FLAGS_PROPERTY] = queryFlags;
// setup the JSON parameters so that OctreeQuery does not use a frustum and uses our JSON filter
_entityViewer.getOctreeQuery().setUsesFrustum(false);
@ -412,6 +419,7 @@ void EntityScriptServer::resetEntitiesScriptEngine() {
connect(newEngine.data(), &ScriptEngine::update, this, [this] {
_entityViewer.queryOctree();
_entityViewer.getTree()->update();
});

View file

@ -0,0 +1,30 @@
//
// EntityNodeData.cpp
// libraries/entities/src
//
// Created by Stephen Birarda on 2/15/17
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "EntityNodeData.h"
bool EntityNodeData::insertFlaggedExtraEntity(const QUuid& filteredEntityID, const QUuid& extraEntityID) {
_flaggedExtraEntities[filteredEntityID].insert(extraEntityID);
return !_previousFlaggedExtraEntities[filteredEntityID].contains(extraEntityID);
}
bool EntityNodeData::isEntityFlaggedAsExtra(const QUuid& entityID) const {
// enumerate each of the sets for the entities that matched our filter
// and immediately return true if any of them contain this entity ID
foreach(QSet<QUuid> entitySet, _flaggedExtraEntities) {
if (entitySet.contains(entityID)) {
return true;
}
}
return false;
}

View file

@ -1,6 +1,6 @@
//
// EntityNodeData.h
// assignment-client/src/entities
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 4/29/14
// Copyright 2014 High Fidelity, Inc.
@ -16,6 +16,13 @@
#include <OctreeQueryNode.h>
namespace EntityJSONQueryProperties {
static const QString SERVER_SCRIPTS_PROPERTY = "serverScripts";
static const QString FLAGS_PROPERTY = "flags";
static const QString INCLUDE_ANCESTORS_PROPERTY = "includeAncestors";
static const QString INCLUDE_DESCENDANTS_PROPERTY = "includeDescendants";
}
class EntityNodeData : public OctreeQueryNode {
public:
virtual PacketType getMyPacketType() const override { return PacketType::EntityData; }
@ -24,13 +31,24 @@ public:
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }
// these can only be called from the OctreeSendThread for the given Node
void insertEntitySentLastFrame(const QUuid& entityID) { _entitiesSentLastFrame.insert(entityID); }
void removeEntitySentLastFrame(const QUuid& entityID) { _entitiesSentLastFrame.remove(entityID); }
bool sentEntityLastFrame(const QUuid& entityID) { return _entitiesSentLastFrame.contains(entityID); }
void insertSentFilteredEntity(const QUuid& entityID) { _sentFilteredEntities.insert(entityID); }
void removeSentFilteredEntity(const QUuid& entityID) { _sentFilteredEntities.remove(entityID); }
bool sentFilteredEntity(const QUuid& entityID) { return _sentFilteredEntities.contains(entityID); }
QSet<QUuid> getSentFilteredEntities() { return _sentFilteredEntities; }
// the following flagged extra entity methods can only be called from the OctreeSendThread for the given Node
// inserts the extra entity and returns a boolean indicating wether the extraEntityID was a new addition
bool insertFlaggedExtraEntity(const QUuid& filteredEntityID, const QUuid& extraEntityID);
bool isEntityFlaggedAsExtra(const QUuid& entityID) const;
void resetFlaggedExtraEntities() { _previousFlaggedExtraEntities = _flaggedExtraEntities; _flaggedExtraEntities.clear(); }
private:
quint64 _lastDeletedEntitiesSentAt { usecTimestampNow() };
QSet<QUuid> _entitiesSentLastFrame;
QSet<QUuid> _sentFilteredEntities;
QHash<QUuid, QSet<QUuid>> _flaggedExtraEntities;
QHash<QUuid, QSet<QUuid>> _previousFlaggedExtraEntities;
};
#endif // hifi_EntityNodeData_h

View file

@ -310,16 +310,17 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
if (entityMatchesFilters) {
// make sure this entity is in the set of entities sent last frame
entityNodeData->insertEntitySentLastFrame(entity->getID());
} else {
// we might include this entity if it matched in the previous frame
if (entityNodeData->sentEntityLastFrame(entity->getID())) {
entityNodeData->removeEntitySentLastFrame(entity->getID());
} else {
includeThisEntity = false;
}
entityNodeData->insertSentFilteredEntity(entity->getID());
} else if (entityNodeData->sentFilteredEntity(entity->getID())) {
// this entity matched in the previous frame - we send it still so the client realizes it just
// fell outside of their filter
entityNodeData->removeSentFilteredEntity(entity->getID());
} else if (!entityNodeData->isEntityFlaggedAsExtra(entity->getID())) {
// we don't send this entity because
// (1) it didn't match our filter
// (2) it didn't match our filter last frame
// (3) it isn't one the JSON query flags told us we should still include
includeThisEntity = false;
}
}

View file

@ -51,7 +51,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityPhysics:
return VERSION_ENTITIES_ZONE_FILTERS;
case PacketType::EntityQuery:
return static_cast<PacketVersion>(EntityQueryPacketVersion::JsonFilter);
return static_cast<PacketVersion>(EntityQueryPacketVersion::JSONFilterWithFamilyTree);
case PacketType::AvatarIdentity:
case PacketType::AvatarData:
case PacketType::BulkAvatarData:

View file

@ -207,7 +207,8 @@ const PacketVersion VERSION_ENTITIES_PHYSICS_PACKET = 67;
const PacketVersion VERSION_ENTITIES_ZONE_FILTERS = 68;
enum class EntityQueryPacketVersion: PacketVersion {
JsonFilter = 18
JSONFilter = 18,
JSONFilterWithFamilyTree = 19
};
enum class AssetServerPacketVersion: PacketVersion {

View file

@ -103,6 +103,9 @@ public:
// call only from OctreeSendThread for the given node
bool haveJSONParametersChanged();
bool shouldForceFullScene() const { return _shouldForceFullScene; }
void setShouldForceFullScene(bool shouldForceFullScene) { _shouldForceFullScene = shouldForceFullScene; }
private:
OctreeQueryNode(const OctreeQueryNode &);
OctreeQueryNode& operator= (const OctreeQueryNode&);
@ -148,6 +151,8 @@ private:
std::array<char, udt::MAX_PACKET_SIZE> _lastOctreePayload;
QJsonObject _lastCheckJSONParameters;
bool _shouldForceFullScene { false };
};
#endif // hifi_OctreeQueryNode_h

View file

@ -70,6 +70,9 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) {
_parentKnowsMe = false;
}
});
bool success = false;
getParentPointer(success);
}
Transform SpatiallyNestable::getParentTransform(bool& success, int depth) const {