From b9f9cddea0b3206fdb363a76160988c87bba5584 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 1 Jun 2017 16:40:16 -0700 Subject: [PATCH 1/7] Add versioning support to the KTX cache --- .../src/model-networking/KTXCache.cpp | 14 ++++++ .../src/model-networking/KTXCache.h | 5 +++ libraries/networking/src/FileCache.cpp | 45 +++++++++++++------ libraries/networking/src/FileCache.h | 16 +++++-- tests/networking/src/FileCacheTests.cpp | 27 ++++++++--- tests/networking/src/FileCacheTests.h | 1 + 6 files changed, 85 insertions(+), 23 deletions(-) diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index e0447af8e6..e6d6632571 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -11,14 +11,28 @@ #include "KTXCache.h" +#include #include using File = cache::File; using FilePointer = cache::FilePointer; +// Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible, +// this value should be incremented. This will force the KTX cache to be wiped +const int KTXCache::CURRENT_VERSION = 0x01; +const int KTXCache::INVALID_VERSION = 0x00; + + KTXCache::KTXCache(const std::string& dir, const std::string& ext) : FileCache(dir, ext) { initialize(); + + Setting::Handle cacheVersionHandle("hifi.ktx.cache_version", KTXCache::INVALID_VERSION); + auto cacheVersion = cacheVersionHandle.get(); + if (cacheVersion != CURRENT_VERSION) { + wipe(); + cacheVersionHandle.set(CURRENT_VERSION); + } } KTXFilePointer KTXCache::writeFile(const char* data, Metadata&& metadata) { diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index bbf7ceadea..2a3f191b53 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -27,6 +27,11 @@ class KTXCache : public cache::FileCache { Q_OBJECT public: + // Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible, + // this value should be incremented. This will force the KTX cache to be wiped + static const int CURRENT_VERSION; + static const int INVALID_VERSION; + KTXCache(const std::string& dir, const std::string& ext); KTXFilePointer writeFile(const char* data, Metadata&& metadata); diff --git a/libraries/networking/src/FileCache.cpp b/libraries/networking/src/FileCache.cpp index 6cb8cd8f7c..95304e3866 100644 --- a/libraries/networking/src/FileCache.cpp +++ b/libraries/networking/src/FileCache.cpp @@ -236,6 +236,28 @@ namespace cache { }; } +void FileCache::eject(const FilePointer& file) { + file->_cache = nullptr; + const auto& length = file->getLength(); + const auto& key = file->getKey(); + + { + Lock lock(_filesMutex); + if (0 != _files.erase(key)) { + _numTotalFiles -= 1; + _totalFilesSize -= length; + } + } + + { + Lock unusedLock(_unusedFilesMutex); + if (0 != _unusedFiles.erase(file)) { + _numUnusedFiles -= 1; + _unusedFilesSize -= length; + } + } +} + void FileCache::clean() { size_t overbudgetAmount = getOverbudgetAmount(); @@ -250,28 +272,23 @@ void FileCache::clean() { for (const auto& file : _unusedFiles) { queue.push(file); } + while (!queue.empty() && overbudgetAmount > 0) { auto file = queue.top(); queue.pop(); + eject(file); auto length = file->getLength(); - - unusedLock.unlock(); - { - file->_cache = nullptr; - Lock lock(_filesMutex); - _files.erase(file->getKey()); - } - unusedLock.lock(); - - _unusedFiles.erase(file); - _numTotalFiles -= 1; - _numUnusedFiles -= 1; - _totalFilesSize -= length; - _unusedFilesSize -= length; overbudgetAmount -= std::min(length, overbudgetAmount); } } +void FileCache::wipe() { + Lock unusedFilesLock(_unusedFilesMutex); + while (!_unusedFiles.empty()) { + eject(*_unusedFiles.begin()); + } +} + void FileCache::clear() { // Eliminate any overbudget files clean(); diff --git a/libraries/networking/src/FileCache.h b/libraries/networking/src/FileCache.h index 040e1ab592..f29d75f779 100644 --- a/libraries/networking/src/FileCache.h +++ b/libraries/networking/src/FileCache.h @@ -46,6 +46,9 @@ public: FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); virtual ~FileCache(); + // Remove all unlocked items from the cache + void wipe(); + size_t getNumTotalFiles() const { return _numTotalFiles; } size_t getNumCachedFiles() const { return _numUnusedFiles; } size_t getSizeTotalFiles() const { return _totalFilesSize; } @@ -95,6 +98,9 @@ public: private: using Mutex = std::recursive_mutex; using Lock = std::unique_lock; + using Map = std::unordered_map>; + using Set = std::unordered_set; + using KeySet = std::unordered_set; friend class File; @@ -105,6 +111,8 @@ private: void removeUnusedFile(const FilePointer& file); void clean(); void clear(); + // Remove a file from the cache + void eject(const FilePointer& file); size_t getOverbudgetAmount() const; @@ -122,10 +130,10 @@ private: std::string _dirpath; bool _initialized { false }; - std::unordered_map> _files; + Map _files; Mutex _filesMutex; - std::unordered_set _unusedFiles; + Set _unusedFiles; Mutex _unusedFilesMutex; }; @@ -136,8 +144,8 @@ public: using Key = FileCache::Key; using Metadata = FileCache::Metadata; - Key getKey() const { return _key; } - size_t getLength() const { return _length; } + const Key& getKey() const { return _key; } + const size_t& getLength() const { return _length; } std::string getFilepath() const { return _filepath; } virtual ~File(); diff --git a/tests/networking/src/FileCacheTests.cpp b/tests/networking/src/FileCacheTests.cpp index 0813d05a54..79fe9dee54 100644 --- a/tests/networking/src/FileCacheTests.cpp +++ b/tests/networking/src/FileCacheTests.cpp @@ -113,18 +113,21 @@ void FileCacheTests::testUnusedFiles() { QVERIFY(!file.get()); } - QThread::msleep(1000); // Test files 90 to 99 are present for (int i = 90; i < 100; ++i) { std::string key = getFileKey(i); auto file = cache->getFile(key); QVERIFY(file.get()); inUseFiles.push_back(file); - // Each access touches the file, so we need to sleep here to ensure that the files are - // spaced out in numeric order, otherwise later tests can't reliably determine the order - // for cache ejection - QThread::msleep(1000); + + if (i == 94) { + // Each access touches the file, so we need to sleep here to ensure that the the last 5 files + // have later times for cache ejection priority, otherwise the test runs too fast to reliably + // differentiate + QThread::msleep(1000); + } } + QCOMPARE(cache->getNumCachedFiles(), (size_t)0); QCOMPARE(cache->getNumTotalFiles(), (size_t)10); inUseFiles.clear(); @@ -165,6 +168,20 @@ void FileCacheTests::testFreeSpacePreservation() { } } +void FileCacheTests::testWipe() { + // Reset the cache + auto cache = makeFileCache(_testDir.path()); + QCOMPARE(cache->getNumCachedFiles(), (size_t)5); + QCOMPARE(cache->getNumTotalFiles(), (size_t)5); + cache->wipe(); + QCOMPARE(cache->getNumCachedFiles(), (size_t)0); + QCOMPARE(cache->getNumTotalFiles(), (size_t)0); + QVERIFY(getCacheDirectorySize() > 0); + forceDeletes(); + QCOMPARE(getCacheDirectorySize(), (size_t)0); +} + + void FileCacheTests::cleanupTestCase() { } diff --git a/tests/networking/src/FileCacheTests.h b/tests/networking/src/FileCacheTests.h index 838c15afb8..b34b384855 100644 --- a/tests/networking/src/FileCacheTests.h +++ b/tests/networking/src/FileCacheTests.h @@ -20,6 +20,7 @@ private slots: void testUnusedFiles(); void testFreeSpacePreservation(); void cleanupTestCase(); + void testWipe(); private: size_t getFreeSpace() const; From c56a6c44f85e93ad4014607433bb976fda16ba71 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 1 Jun 2017 10:03:50 -0700 Subject: [PATCH 2/7] minor formatting fix --- libraries/entities/src/EntityItem.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 431b87cc61..640eba61d8 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -681,7 +681,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef // and pretend that we own it (we assume we'll recover it soon) // However, for now, when the server uses a newer time than what we sent, listen to what we're told. - if (overwriteLocalData) weOwnSimulation = false; + if (overwriteLocalData) { + weOwnSimulation = false; + } } else if (_simulationOwner.set(newSimOwner)) { markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID); somethingChanged = true; From e34f979ed925c255eaaaa6e1311105db4256b132 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 1 Jun 2017 10:04:31 -0700 Subject: [PATCH 3/7] add EntityMotionState::isLocallyOwned() for cleaner logic --- libraries/physics/src/EntityMotionState.cpp | 14 +++++++++----- libraries/physics/src/EntityMotionState.h | 1 + libraries/physics/src/ObjectMotionState.h | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index fdd290bfca..50209a2101 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -84,7 +84,7 @@ EntityMotionState::~EntityMotionState() { void EntityMotionState::updateServerPhysicsVariables() { assert(entityTreeIsLocked()); - if (_entity->getSimulatorID() == Physics::getSessionUUID()) { + if (isLocallyOwned()) { // don't slam these values if we are the simulation owner return; } @@ -135,7 +135,7 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; } _loopsWithoutOwner = 0; - } else if (_entity->getSimulatorID() == Physics::getSessionUUID()) { + } else if (isLocallyOwned()) { // we just inherited ownership, make sure our desired priority matches what we have upgradeOutgoingPriority(_entity->getSimulationPriority()); } else { @@ -315,7 +315,7 @@ bool EntityMotionState::isCandidateForOwnership() const { assert(_entity); assert(entityTreeIsLocked()); return _outgoingPriority != 0 - || Physics::getSessionUUID() == _entity->getSimulatorID() + || isLocallyOwned() || _entity->dynamicDataNeedsTransmit(); } @@ -489,7 +489,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { return true; } - if (_entity->getSimulatorID() != Physics::getSessionUUID()) { + if (!isLocallyOwned()) { // we don't own the simulation // NOTE: we do not volunteer to own kinematic or static objects @@ -597,7 +597,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ properties.clearSimulationOwner(); _outgoingPriority = 0; _entity->setPendingOwnershipPriority(_outgoingPriority, now); - } else if (Physics::getSessionUUID() != _entity->getSimulatorID()) { + } else if (!isLocallyOwned()) { // we don't own the simulation for this entity yet, but we're sending a bid for it quint8 bidPriority = glm::max(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY); properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority); @@ -786,6 +786,10 @@ void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& ma _entity->computeCollisionGroupAndFinalMask(group, mask); } +bool EntityMotionState::isLocallyOwned() const { + return _entity->getSimulatorID() == Physics::getSessionUUID(); +} + bool EntityMotionState::shouldBeLocallyOwned() const { return (_outgoingPriority > VOLUNTEER_SIMULATION_PRIORITY && _outgoingPriority > _entity->getSimulationPriority()) || _entity->getSimulatorID() == Physics::getSessionUUID(); diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index 380edf3927..541ad7c93c 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -79,6 +79,7 @@ public: virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override; + bool isLocallyOwned() const override; bool shouldBeLocallyOwned() const override; friend class PhysicalEntitySimulation; diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 1e582ea854..a12a2eaa01 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -146,6 +146,7 @@ public: void dirtyInternalKinematicChanges() { _hasInternalKinematicChanges = true; } void clearInternalKinematicChanges() { _hasInternalKinematicChanges = false; } + virtual bool isLocallyOwned() const { return false; } virtual bool shouldBeLocallyOwned() const { return false; } friend class PhysicsEngine; From 74827fc4c802ea3c60a4031818dc76502d80d007 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 1 Jun 2017 10:05:26 -0700 Subject: [PATCH 4/7] recently moved static objects added to changelist since static objects are not active this will make the interface release ownership of recently changed static objects --- libraries/physics/src/PhysicsEngine.cpp | 43 +++++++++++-------- libraries/physics/src/PhysicsEngine.h | 3 +- .../physics/src/ThreadSafeDynamicsWorld.h | 2 + 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 3a02e95e7c..1c6faada1d 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -129,6 +129,9 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { } body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); body->updateInertiaTensor(); + if (motionState->isLocallyOwned()) { + _activeStaticBodies.insert(body); + } break; } } @@ -174,19 +177,9 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) { // frame (because the framerate is faster than our physics simulation rate). When this happens we must scan // _activeStaticBodies for objects that were recently deleted so we don't try to access a dangling pointer. for (auto object : objects) { - btRigidBody* body = object->getRigidBody(); - - std::vector::reverse_iterator itr = _activeStaticBodies.rbegin(); - while (itr != _activeStaticBodies.rend()) { - if (body == *itr) { - if (*itr != *(_activeStaticBodies.rbegin())) { - // swap with rbegin - *itr = *(_activeStaticBodies.rbegin()); - } - _activeStaticBodies.pop_back(); - break; - } - ++itr; + std::set::iterator itr = _activeStaticBodies.find(object->getRigidBody()); + if (itr != _activeStaticBodies.end()) { + _activeStaticBodies.erase(itr); } } } @@ -245,14 +238,16 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob object->clearIncomingDirtyFlags(); } if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) { - _activeStaticBodies.push_back(object->getRigidBody()); + _activeStaticBodies.insert(object->getRigidBody()); } } // active static bodies have changed (in an Easy way) and need their Aabbs updated // but we've configured Bullet to NOT update them automatically (for improved performance) // so we must do it ourselves - for (size_t i = 0; i < _activeStaticBodies.size(); ++i) { - _dynamicsWorld->updateSingleAabb(_activeStaticBodies[i]); + std::set::const_iterator itr = _activeStaticBodies.begin(); + while (itr != _activeStaticBodies.end()) { + _dynamicsWorld->updateSingleAabb(*itr); + ++itr; } return stillNeedChange; } @@ -496,13 +491,23 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() { const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() { BT_PROFILE("copyOutgoingChanges"); + + _dynamicsWorld->synchronizeMotionStates(); + // Bullet will not deactivate static objects (it doesn't expect them to be active) // so we must deactivate them ourselves - for (size_t i = 0; i < _activeStaticBodies.size(); ++i) { - _activeStaticBodies[i]->forceActivationState(ISLAND_SLEEPING); + std::set::const_iterator itr = _activeStaticBodies.begin(); + while (itr != _activeStaticBodies.end()) { + btRigidBody* body = *itr; + body->forceActivationState(ISLAND_SLEEPING); + ObjectMotionState* motionState = static_cast(body->getUserPointer()); + if (motionState) { + _dynamicsWorld->addChangedMotionState(motionState); + } + ++itr; } _activeStaticBodies.clear(); - _dynamicsWorld->synchronizeMotionStates(); + _hasOutgoingChanges = false; return _dynamicsWorld->getChangedMotionStates(); } diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index e9b29a43a4..b20d73f0e6 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -13,6 +13,7 @@ #define hifi_PhysicsEngine_h #include +#include #include #include @@ -114,7 +115,7 @@ private: CollisionEvents _collisionEvents; QHash _objectDynamics; QHash> _objectDynamicsByBody; - std::vector _activeStaticBodies; + std::set _activeStaticBodies; glm::vec3 _originOffset; diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index b4fcca8cdb..54c3ddb756 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -51,6 +51,8 @@ public: const VectorOfMotionStates& getChangedMotionStates() const { return _changedMotionStates; } const VectorOfMotionStates& getDeactivatedMotionStates() const { return _deactivatedStates; } + void addChangedMotionState(ObjectMotionState* motionState) { _changedMotionStates.push_back(motionState); } + private: // call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState() void synchronizeMotionState(btRigidBody* body); From 29a05d7eb4832102b8e6bf65f6a41514c288e63f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 1 Jun 2017 14:17:14 -0700 Subject: [PATCH 5/7] bid immediately when creating new entity also clear EntityMotionState::_numInactiveUpdates on new ownership --- libraries/entities/src/EntityItem.cpp | 20 ++++------------- libraries/entities/src/EntityItem.h | 4 ++-- .../entities/src/EntityScriptingInterface.cpp | 10 +++++---- libraries/entities/src/SimulationFlags.h | 4 +--- libraries/entities/src/SimulationOwner.cpp | 22 +++++++++---------- libraries/entities/src/SimulationOwner.h | 5 +++-- libraries/physics/src/EntityMotionState.cpp | 21 +++++++++--------- 7 files changed, 38 insertions(+), 48 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 640eba61d8..530e356137 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1295,27 +1295,15 @@ void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) c properties._accelerationChanged = true; } -void EntityItem::pokeSimulationOwnership() { - markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_POKE); +void EntityItem::flagForOwnershipBid(uint8_t priority) { + markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY); auto nodeList = DependencyManager::get(); if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) { // we already own it - _simulationOwner.promotePriority(SCRIPT_POKE_SIMULATION_PRIORITY); + _simulationOwner.promotePriority(priority); } else { // we don't own it yet - _simulationOwner.setPendingPriority(SCRIPT_POKE_SIMULATION_PRIORITY, usecTimestampNow()); - } -} - -void EntityItem::grabSimulationOwnership() { - markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB); - auto nodeList = DependencyManager::get(); - if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) { - // we already own it - _simulationOwner.promotePriority(SCRIPT_GRAB_SIMULATION_PRIORITY); - } else { - // we don't own it yet - _simulationOwner.setPendingPriority(SCRIPT_GRAB_SIMULATION_PRIORITY, usecTimestampNow()); + _simulationOwner.setPendingPriority(priority, usecTimestampNow()); } } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 3243f50556..7c08137a1c 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -321,6 +321,7 @@ public: void updateSimulationOwner(const SimulationOwner& owner); void clearSimulationOwnership(); void setPendingOwnershipPriority(quint8 priority, const quint64& timestamp); + uint8_t getPendingOwnershipPriority() const { return _simulationOwner.getPendingPriority(); } void rememberHasSimulationOwnershipBid() const; QString getMarketplaceID() const; @@ -394,8 +395,7 @@ public: void getAllTerseUpdateProperties(EntityItemProperties& properties) const; - void pokeSimulationOwnership(); - void grabSimulationOwnership(); + void flagForOwnershipBid(uint8_t priority); void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } QString actionsToDebugString(); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index b184d648da..90dc6893b4 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -230,6 +230,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties } entity->setLastBroadcast(usecTimestampNow()); + // since we're creating this object we will immediately volunteer to own its simulation + entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY); propertiesWithSimID.setLastEdited(entity->getLastEdited()); } else { qCDebug(entities) << "script failed to add new Entity to local Octree"; @@ -440,7 +442,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } else { // we make a bid for simulation ownership properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY); - entity->pokeSimulationOwnership(); + entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY); entity->rememberHasSimulationOwnershipBid(); } } @@ -1194,7 +1196,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, } action->setIsMine(true); success = entity->addAction(simulation, action); - entity->grabSimulationOwnership(); + entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY); return false; // Physics will cause a packet to be sent, so don't send from here. }); if (success) { @@ -1210,7 +1212,7 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) { bool success = entity->updateAction(simulation, actionID, arguments); if (success) { - entity->grabSimulationOwnership(); + entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY); } return success; }); @@ -1224,7 +1226,7 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& success = entity->removeAction(simulation, actionID); if (success) { // reduce from grab to poke - entity->pokeSimulationOwnership(); + entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY); } return false; // Physics will cause a packet to be sent, so don't send from here. }); diff --git a/libraries/entities/src/SimulationFlags.h b/libraries/entities/src/SimulationFlags.h index dbcf51afc0..e2b2224b4a 100644 --- a/libraries/entities/src/SimulationFlags.h +++ b/libraries/entities/src/SimulationFlags.h @@ -26,12 +26,10 @@ namespace Simulation { const uint32_t DIRTY_MATERIAL = 0x00400; const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed - const uint32_t DIRTY_SIMULATION_OWNERSHIP_FOR_POKE = 0x2000; // bid for simulation ownership at "poke" - const uint32_t DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB = 0x4000; // bid for simulation ownership at "grab" + const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; - const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = DIRTY_SIMULATION_OWNERSHIP_FOR_POKE | DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB; }; #endif // hifi_SimulationFlags_h diff --git a/libraries/entities/src/SimulationOwner.cpp b/libraries/entities/src/SimulationOwner.cpp index 77645a9e62..4398582673 100644 --- a/libraries/entities/src/SimulationOwner.cpp +++ b/libraries/entities/src/SimulationOwner.cpp @@ -26,9 +26,9 @@ const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1; SimulationOwner::SimulationOwner() : _id(), _expiry(0), - _pendingTimestamp(0), + _pendingBidTimestamp(0), _priority(0), - _pendingPriority(0), + _pendingBidPriority(0), _pendingState(PENDING_STATE_NOTHING) { } @@ -36,9 +36,9 @@ SimulationOwner::SimulationOwner() : SimulationOwner::SimulationOwner(const QUuid& id, quint8 priority) : _id(id), _expiry(0), - _pendingTimestamp(0), + _pendingBidTimestamp(0), _priority(priority), - _pendingPriority(0) + _pendingBidPriority(0) { } @@ -61,9 +61,9 @@ bool SimulationOwner::fromByteArray(const QByteArray& data) { void SimulationOwner::clear() { _id = QUuid(); _expiry = 0; - _pendingTimestamp = 0; + _pendingBidTimestamp = 0; _priority = 0; - _pendingPriority = 0; + _pendingBidPriority = 0; _pendingState = PENDING_STATE_NOTHING; } @@ -102,9 +102,9 @@ bool SimulationOwner::set(const SimulationOwner& owner) { } void SimulationOwner::setPendingPriority(quint8 priority, const quint64& timestamp) { - _pendingPriority = priority; - _pendingTimestamp = timestamp; - _pendingState = (_pendingPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE; + _pendingBidPriority = priority; + _pendingBidTimestamp = timestamp; + _pendingState = (_pendingBidPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE; } void SimulationOwner::updateExpiry() { @@ -113,11 +113,11 @@ void SimulationOwner::updateExpiry() { } bool SimulationOwner::pendingRelease(const quint64& timestamp) { - return _pendingPriority == 0 && _pendingState == PENDING_STATE_RELEASE && _pendingTimestamp >= timestamp; + return _pendingBidPriority == 0 && _pendingState == PENDING_STATE_RELEASE && _pendingBidTimestamp >= timestamp; } bool SimulationOwner::pendingTake(const quint64& timestamp) { - return _pendingPriority > 0 && _pendingState == PENDING_STATE_TAKE && _pendingTimestamp >= timestamp; + return _pendingBidPriority > 0 && _pendingState == PENDING_STATE_TAKE && _pendingBidTimestamp >= timestamp; } void SimulationOwner::clearCurrentOwner() { diff --git a/libraries/entities/src/SimulationOwner.h b/libraries/entities/src/SimulationOwner.h index 5f940bbe25..94ed1d9a08 100644 --- a/libraries/entities/src/SimulationOwner.h +++ b/libraries/entities/src/SimulationOwner.h @@ -66,6 +66,7 @@ public: bool hasExpired() const { return usecTimestampNow() > _expiry; } + uint8_t getPendingPriority() const { return _pendingBidPriority; } bool pendingRelease(const quint64& timestamp); // return true if valid pending RELEASE bool pendingTake(const quint64& timestamp); // return true if valid pending TAKE void clearCurrentOwner(); @@ -84,9 +85,9 @@ public: private: QUuid _id; // owner quint64 _expiry; // time when ownership can transition at equal priority - quint64 _pendingTimestamp; // time when pending update was set + quint64 _pendingBidTimestamp; // time when pending bid was set quint8 _priority; // priority of current owner - quint8 _pendingPriority; // priority of pendingTake + quint8 _pendingBidPriority; // priority at which we'd like to own it quint8 _pendingState; // NOTHING, TAKE, or RELEASE }; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 50209a2101..d4be72e699 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -65,8 +65,7 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer _lastStep(0), _loopsWithoutOwner(0), _accelerationNearlyGravityCount(0), - _numInactiveUpdates(1), - _outgoingPriority(0) + _numInactiveUpdates(1) { _type = MOTIONSTATE_TYPE_ENTITY; assert(_entity); @@ -75,6 +74,8 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer // we need the side-effects of EntityMotionState::setShape() so we call it explicitly here // rather than pass the legit shape pointer to the ObjectMotionState ctor above. setShape(shape); + + _outgoingPriority = _entity->getPendingOwnershipPriority(); } EntityMotionState::~EntityMotionState() { @@ -135,23 +136,23 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; } _loopsWithoutOwner = 0; + _numInactiveUpdates = 0; } else if (isLocallyOwned()) { // we just inherited ownership, make sure our desired priority matches what we have upgradeOutgoingPriority(_entity->getSimulationPriority()); } else { _outgoingPriority = 0; _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; + _numInactiveUpdates = 0; } } if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY) { - // The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bits really mean "we should bid for ownership because - // a local script has been changing physics properties, or we should adjust our own ownership priority". - // The desired priority is determined by which bits were set. - if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB) { - _outgoingPriority = SCRIPT_GRAB_SIMULATION_PRIORITY; - } else { - _outgoingPriority = SCRIPT_POKE_SIMULATION_PRIORITY; - } + // The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bit means one of the following: + // (1) we own it but may need to change the priority OR... + // (2) we don't own it but should bid (because a local script has been changing physics properties) + uint8_t newPriority = isLocallyOwned() ? _entity->getSimulationOwner().getPriority() : _entity->getSimulationOwner().getPendingPriority(); + _outgoingPriority = glm::max(_outgoingPriority, newPriority); + // reset bid expiry so that we bid ASAP _nextOwnershipBid = 0; } From 0fdbf4a34292d024415d2e30198d9e627845e815 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 2 Jun 2017 09:50:23 -0700 Subject: [PATCH 6/7] Add startup_sent_to user activity event --- interface/src/Application.cpp | 36 +++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8c6bea0905..ba9fd45ef7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2384,15 +2384,16 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { // Check HMD use (may be technically available without being in use) bool hasHMD = PluginUtils::isHMDAvailable(); - bool isUsingHMD = hasHMD && hasHandControllers && _displayPlugin->isHmd(); + bool isUsingHMD = _displayPlugin->isHmd(); + bool isUsingHMDAndHandControllers = hasHMD && hasHandControllers && isUsingHMD; Setting::Handle tutorialComplete{ "tutorialComplete", false }; Setting::Handle firstRun{ Settings::firstRun, true }; bool isTutorialComplete = tutorialComplete.get(); - bool shouldGoToTutorial = isUsingHMD && hasTutorialContent && !isTutorialComplete; + bool shouldGoToTutorial = isUsingHMDAndHandControllers && hasTutorialContent && !isTutorialComplete; - qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMD; + qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers; qCDebug(interfaceapp) << "Tutorial version:" << contentVersion << ", sufficient:" << hasTutorialContent << ", complete:" << isTutorialComplete << ", should go:" << shouldGoToTutorial; @@ -2406,10 +2407,18 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { const QString TUTORIAL_PATH = "/tutorial_begin"; + static const QString SENT_TO_TUTORIAL = "tutorial"; + static const QString SENT_TO_PREVIOUS_LOCATION = "previous_location"; + static const QString SENT_TO_ENTRY = "entry"; + static const QString SENT_TO_SANDBOX = "sandbox"; + + QString sentTo; + if (shouldGoToTutorial) { if (sandboxIsRunning) { qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; DependencyManager::get()->goToLocalSandbox(TUTORIAL_PATH); + sentTo = SENT_TO_TUTORIAL; } else { qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; if (firstRun.get()) { @@ -2417,8 +2426,10 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { } if (addressLookupString.isEmpty()) { DependencyManager::get()->goToEntry(); + sentTo = SENT_TO_ENTRY; } else { DependencyManager::get()->loadSettings(addressLookupString); + sentTo = SENT_TO_PREVIOUS_LOCATION; } } } else { @@ -2431,23 +2442,40 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { // If this is a first run we short-circuit the address passed in if (isFirstRun) { - if (isUsingHMD) { + if (isUsingHMDAndHandControllers) { if (sandboxIsRunning) { qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; DependencyManager::get()->goToLocalSandbox(); + sentTo = SENT_TO_SANDBOX; } else { qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; DependencyManager::get()->goToEntry(); + sentTo = SENT_TO_ENTRY; } } else { DependencyManager::get()->goToEntry(); + sentTo = SENT_TO_ENTRY; } } else { qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); DependencyManager::get()->loadSettings(addressLookupString); + sentTo = SENT_TO_PREVIOUS_LOCATION; } } + UserActivityLogger::getInstance().logAction("startup_sent_to", { + { "sent_to", sentTo }, + { "sandbox_is_running", sandboxIsRunning }, + { "has_hmd", hasHMD }, + { "has_hand_controllers", hasHandControllers }, + { "is_using_hmd", isUsingHMD }, + { "is_using_hmd_and_hand_controllers", isUsingHMDAndHandControllers }, + { "content_version", contentVersion }, + { "is_tutorial_complete", isTutorialComplete }, + { "has_tutorial_content", hasTutorialContent }, + { "should_go_to_tutorial", shouldGoToTutorial } + }); + _connectionMonitor.init(); // After all of the constructor is completed, then set firstRun to false. From 1f95daf5c1b39f9d525b1d207df520e5d2c148c3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 2 Jun 2017 11:19:43 -0700 Subject: [PATCH 7/7] Add dev menu option to wipe KTX cache on next launch --- interface/src/Menu.cpp | 6 ++++++ interface/src/Menu.h | 1 + .../model-networking/src/model-networking/KTXCache.cpp | 4 ++-- libraries/model-networking/src/model-networking/KTXCache.h | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 4138798484..463069430d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -407,6 +407,12 @@ Menu::Menu() { #endif + { + auto action = addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderClearKtxCache); + connect(action, &QAction::triggered, []{ + Setting::Handle(KTXCache::SETTING_VERSION_NAME, KTXCache::INVALID_VERSION).set(KTXCache::INVALID_VERSION); + }); + } // Developer > Render > LOD Tools addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b6d72f5446..41bf89a20a 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -145,6 +145,7 @@ namespace MenuOption { const QString Quit = "Quit"; const QString ReloadAllScripts = "Reload All Scripts"; const QString ReloadContent = "Reload Content (Clears all caches)"; + const QString RenderClearKtxCache = "Clear KTX Cache (requires restart)"; const QString RenderMaxTextureMemory = "Maximum Texture Memory"; const QString RenderMaxTextureAutomatic = "Automatic Texture Memory"; const QString RenderMaxTexture4MB = "4 MB"; diff --git a/libraries/model-networking/src/model-networking/KTXCache.cpp b/libraries/model-networking/src/model-networking/KTXCache.cpp index e6d6632571..6443748920 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.cpp +++ b/libraries/model-networking/src/model-networking/KTXCache.cpp @@ -21,13 +21,13 @@ using FilePointer = cache::FilePointer; // this value should be incremented. This will force the KTX cache to be wiped const int KTXCache::CURRENT_VERSION = 0x01; const int KTXCache::INVALID_VERSION = 0x00; - +const char* KTXCache::SETTING_VERSION_NAME = "hifi.ktx.cache_version"; KTXCache::KTXCache(const std::string& dir, const std::string& ext) : FileCache(dir, ext) { initialize(); - Setting::Handle cacheVersionHandle("hifi.ktx.cache_version", KTXCache::INVALID_VERSION); + Setting::Handle cacheVersionHandle(SETTING_VERSION_NAME, INVALID_VERSION); auto cacheVersion = cacheVersionHandle.get(); if (cacheVersion != CURRENT_VERSION) { wipe(); diff --git a/libraries/model-networking/src/model-networking/KTXCache.h b/libraries/model-networking/src/model-networking/KTXCache.h index 2a3f191b53..5617019c52 100644 --- a/libraries/model-networking/src/model-networking/KTXCache.h +++ b/libraries/model-networking/src/model-networking/KTXCache.h @@ -31,6 +31,7 @@ public: // this value should be incremented. This will force the KTX cache to be wiped static const int CURRENT_VERSION; static const int INVALID_VERSION; + static const char* SETTING_VERSION_NAME; KTXCache(const std::string& dir, const std::string& ext);