History

Merge pull request #14042 from thoys/perf/create/getMultipleEntities

Speed up Entity List properties fetching by adding Entities.getMultipleEntities API

Brad Hefta-Gaub committed 8 years ago

Commit edd5db72 parents ef063a4 eeda74036 files · +343 −81Browse files

Showing the changes to libraries/entities/src/EntityScriptingInterface.cpp (1 of 36 files). Show all

Unified · Split

libraries/entities/src/EntityScriptingInterface.cpp+134−6
@@ -42,7 +42,7 @@ const QString GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":true}}"; · 41 unmodified lines
4242 const QString NOT_GRABBABLE_USER_DATA = "{\"grabbableKey\":{\"grabbable\":false}}";
4343
4444 EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership) :
45− _entityTree(NULL),
45+ _entityTree(nullptr),
4646 _bidOnSimulationOwnership(bidOnSimulationOwnership)
4747 {
4848 auto nodeList = DependencyManager::get<NodeList>();
@@ -350,19 +350,19 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { · 301 unmodified lines
350350 }
351351 }
352352
353−EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity) {
354− EntityPropertyFlags noSpecificProperties;
355− return getEntityProperties(identity, noSpecificProperties);
353+EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid entityID) {
354+ const EntityPropertyFlags noSpecificProperties;
355+ return getEntityProperties(entityID, noSpecificProperties);
356356 }
357357
358−EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties) {
358+EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid entityID, EntityPropertyFlags desiredProperties) {
359359 PROFILE_RANGE(script_entities, __FUNCTION__);
360360
361361 bool scalesWithParent { false };
362362 EntityItemProperties results;
363363 if (_entityTree) {
364364 _entityTree->withReadLock([&] {
365− EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity));
365+ EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
366366 if (entity) {
367367 scalesWithParent = entity->getScalesWithParent();
368368 if (desiredProperties.getHasProperty(PROP_POSITION) ||
@@ -397,6 +397,134 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit · 28 unmodified lines
397397 return convertPropertiesToScriptSemantics(results, scalesWithParent);
398398 }
399399
400+
401+struct EntityPropertiesResult {
402+ EntityPropertiesResult(const EntityItemProperties& properties, bool scalesWithParent) :
403+ properties(properties),
404+ scalesWithParent(scalesWithParent) {
405+ }
406+ EntityPropertiesResult() = default;
407+ EntityItemProperties properties;
408+ bool scalesWithParent{ false };
409+};
410+
411+// Static method to make sure that we have the right script engine.
412+// Using sender() or QtScriptable::engine() does not work for classes used by multiple threads (script-engines)
413+QScriptValue EntityScriptingInterface::getMultipleEntityProperties(QScriptContext* context, QScriptEngine* engine) {
414+ const int ARGUMENT_ENTITY_IDS = 0;
415+ const int ARGUMENT_EXTENDED_DESIRED_PROPERTIES = 1;
416+
417+ auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
418+ const auto entityIDs = qScriptValueToValue<QVector<QUuid>>(context->argument(ARGUMENT_ENTITY_IDS));
419+ return entityScriptingInterface->getMultipleEntityPropertiesInternal(engine, entityIDs, context->argument(ARGUMENT_EXTENDED_DESIRED_PROPERTIES));
420+}
421+
422+QScriptValue EntityScriptingInterface::getMultipleEntityPropertiesInternal(QScriptEngine* engine, QVector<QUuid> entityIDs, const QScriptValue& extendedDesiredProperties) {
423+ PROFILE_RANGE(script_entities, __FUNCTION__);
424+
425+ EntityPsuedoPropertyFlags psuedoPropertyFlags;
426+ const auto readExtendedPropertyStringValue = [&](QScriptValue extendedProperty) {
427+ const auto extendedPropertyString = extendedProperty.toString();
428+ if (extendedPropertyString == "id") {
429+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::ID);
430+ } else if (extendedPropertyString == "type") {
431+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::Type);
432+ } else if (extendedPropertyString == "created") {
433+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::Created);
434+ } else if (extendedPropertyString == "age") {
435+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::Age);
436+ } else if (extendedPropertyString == "ageAsText") {
437+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::AgeAsText);
438+ } else if (extendedPropertyString == "lastEdited") {
439+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::LastEdited);
440+ } else if (extendedPropertyString == "boundingBox") {
441+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::BoundingBox);
442+ } else if (extendedPropertyString == "originalTextures") {
443+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::OriginalTextures);
444+ } else if (extendedPropertyString == "renderInfo") {
445+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::RenderInfo);
446+ } else if (extendedPropertyString == "clientOnly") {
447+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::ClientOnly);
448+ } else if (extendedPropertyString == "owningAvatarID") {
449+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::OwningAvatarID);
450+ }
451+ };
452+
453+ if (extendedDesiredProperties.isString()) {
454+ readExtendedPropertyStringValue(extendedDesiredProperties);
455+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::FlagsActive);
456+ } else if (extendedDesiredProperties.isArray()) {
457+ const quint32 length = extendedDesiredProperties.property("length").toInt32();
458+ for (quint32 i = 0; i < length; i++) {
459+ readExtendedPropertyStringValue(extendedDesiredProperties.property(i));
460+ }
461+ psuedoPropertyFlags.set(EntityPsuedoPropertyFlag::FlagsActive);
462+ }
463+
464+ EntityPropertyFlags desiredProperties = qScriptValueToValue<EntityPropertyFlags>(extendedDesiredProperties);
465+ bool needsScriptSemantics = desiredProperties.getHasProperty(PROP_POSITION) ||
466+ desiredProperties.getHasProperty(PROP_ROTATION) ||
467+ desiredProperties.getHasProperty(PROP_LOCAL_POSITION) ||
468+ desiredProperties.getHasProperty(PROP_LOCAL_ROTATION) ||
469+ desiredProperties.getHasProperty(PROP_LOCAL_VELOCITY) ||
470+ desiredProperties.getHasProperty(PROP_LOCAL_ANGULAR_VELOCITY) ||
471+ desiredProperties.getHasProperty(PROP_LOCAL_DIMENSIONS);
472+ if (needsScriptSemantics) {
473+ // if we are explicitly getting position or rotation, we need parent information to make sense of them.
474+ desiredProperties.setHasProperty(PROP_PARENT_ID);
475+ desiredProperties.setHasProperty(PROP_PARENT_JOINT_INDEX);
476+ }
477+ QVector<EntityPropertiesResult> resultProperties;
478+ if (_entityTree) {
479+ PROFILE_RANGE(script_entities, "EntityScriptingInterface::getMultipleEntityProperties>Obtaining Properties");
480+ int i = 0;
481+ const int lockAmount = 500;
482+ int size = entityIDs.size();
483+ while (i < size) {
484+ _entityTree->withReadLock([&] {
485+ for (int j = 0; j < lockAmount && i < size; ++i, ++j) {
486+ const auto& entityID = entityIDs.at(i);
487+ const EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
488+ if (entity) {
489+ if (psuedoPropertyFlags.none() && desiredProperties.isEmpty()) {
490+ // these are left out of EntityItem::getEntityProperties so that localPosition and localRotation
491+ // don't end up in json saves, etc. We still want them here, though.
492+ EncodeBitstreamParams params; // unknown
493+ desiredProperties = entity->getEntityProperties(params);
494+ desiredProperties.setHasProperty(PROP_LOCAL_POSITION);
495+ desiredProperties.setHasProperty(PROP_LOCAL_ROTATION);
496+ desiredProperties.setHasProperty(PROP_LOCAL_VELOCITY);
497+ desiredProperties.setHasProperty(PROP_LOCAL_ANGULAR_VELOCITY);
498+ desiredProperties.setHasProperty(PROP_LOCAL_DIMENSIONS);
499+ psuedoPropertyFlags.set();
500+ needsScriptSemantics = true;
501+ }
502+
503+ auto properties = entity->getProperties(desiredProperties, true);
504+ EntityPropertiesResult result(properties, entity->getScalesWithParent());
505+ resultProperties.append(result);
506+ }
507+ }
508+ });
509+ }
510+ }
511+ QScriptValue finalResult = engine->newArray(resultProperties.size());
512+ quint32 i = 0;
513+ if (needsScriptSemantics) {
514+ PROFILE_RANGE(script_entities, "EntityScriptingInterface::getMultipleEntityProperties>Script Semantics");
515+ foreach(const auto& result, resultProperties) {
516+ finalResult.setProperty(i++, convertPropertiesToScriptSemantics(result.properties, result.scalesWithParent)
517+ .copyToScriptValue(engine, false, false, false, psuedoPropertyFlags));
518+ }
519+ } else {
520+ PROFILE_RANGE(script_entities, "EntityScriptingInterface::getMultipleEntityProperties>Skip Script Semantics");
521+ foreach(const auto& result, resultProperties) {
522+ finalResult.setProperty(i++, result.properties.copyToScriptValue(engine, false, false, false, psuedoPropertyFlags));
523+ }
524+ }
525+ return finalResult;
526+}
527+
400528 QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) {
401529 PROFILE_RANGE(script_entities, __FUNCTION__);
402530