Unified · Split
| @@ -2413,15 +2413,16 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { · 2412 unmodified lines | |||
| 2413 | 2413 | ||
| 2414 | // Check HMD use (may be technically available without being in use) | 2414 | // Check HMD use (may be technically available without being in use) |
| 2415 | bool hasHMD = PluginUtils::isHMDAvailable(); | 2415 | bool hasHMD = PluginUtils::isHMDAvailable(); |
| 2416 | bool isUsingHMD = hasHMD && hasHandControllers && _displayPlugin->isHmd(); | 2416 | bool isUsingHMD = _displayPlugin->isHmd(); |
| 2417 | bool isUsingHMDAndHandControllers = hasHMD && hasHandControllers && isUsingHMD; | ||
| 2417 | 2418 | ||
| 2418 | Setting::Handle<bool> tutorialComplete{ "tutorialComplete", false }; | 2419 | Setting::Handle<bool> tutorialComplete{ "tutorialComplete", false }; |
| 2419 | Setting::Handle<bool> firstRun{ Settings::firstRun, true }; | 2420 | Setting::Handle<bool> firstRun{ Settings::firstRun, true }; |
| 2420 | 2421 | ||
| 2421 | bool isTutorialComplete = tutorialComplete.get(); | 2422 | bool isTutorialComplete = tutorialComplete.get(); |
| 2422 | bool shouldGoToTutorial = isUsingHMD && hasTutorialContent && !isTutorialComplete; | 2423 | bool shouldGoToTutorial = isUsingHMDAndHandControllers && hasTutorialContent && !isTutorialComplete; |
| 2423 | 2424 | ||
| 2424 | qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMD; | 2425 | qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers; |
| 2425 | qCDebug(interfaceapp) << "Tutorial version:" << contentVersion << ", sufficient:" << hasTutorialContent << | 2426 | qCDebug(interfaceapp) << "Tutorial version:" << contentVersion << ", sufficient:" << hasTutorialContent << |
| 2426 | ", complete:" << isTutorialComplete << ", should go:" << shouldGoToTutorial; | 2427 | ", complete:" << isTutorialComplete << ", should go:" << shouldGoToTutorial; |
| 2427 | 2428 | ||
| @@ -2435,10 +2436,18 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { · 7 unmodified lines | |||
| 2435 | 2436 | ||
| 2436 | const QString TUTORIAL_PATH = "/tutorial_begin"; | 2437 | const QString TUTORIAL_PATH = "/tutorial_begin"; |
| 2437 | 2438 | ||
| 2439 | static const QString SENT_TO_TUTORIAL = "tutorial"; | ||
| 2440 | static const QString SENT_TO_PREVIOUS_LOCATION = "previous_location"; | ||
| 2441 | static const QString SENT_TO_ENTRY = "entry"; | ||
| 2442 | static const QString SENT_TO_SANDBOX = "sandbox"; | ||
| 2443 | |||
| 2444 | QString sentTo; | ||
| 2445 | |||
| 2438 | if (shouldGoToTutorial) { | 2446 | if (shouldGoToTutorial) { |
| 2439 | if (sandboxIsRunning) { | 2447 | if (sandboxIsRunning) { |
| 2440 | qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; | 2448 | qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; |
| 2441 | DependencyManager::get<AddressManager>()->goToLocalSandbox(TUTORIAL_PATH); | 2449 | DependencyManager::get<AddressManager>()->goToLocalSandbox(TUTORIAL_PATH); |
| 2450 | sentTo = SENT_TO_TUTORIAL; | ||
| 2442 | } else { | 2451 | } else { |
| 2443 | qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; | 2452 | qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; |
| 2444 | if (firstRun.get()) { | 2453 | if (firstRun.get()) { |
| @@ -2446,8 +2455,10 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { · 1 unmodified line | |||
| 2446 | } | 2455 | } |
| 2447 | if (addressLookupString.isEmpty()) { | 2456 | if (addressLookupString.isEmpty()) { |
| 2448 | DependencyManager::get<AddressManager>()->goToEntry(); | 2457 | DependencyManager::get<AddressManager>()->goToEntry(); |
| 2458 | sentTo = SENT_TO_ENTRY; | ||
| 2449 | } else { | 2459 | } else { |
| 2450 | DependencyManager::get<AddressManager>()->loadSettings(addressLookupString); | 2460 | DependencyManager::get<AddressManager>()->loadSettings(addressLookupString); |
| 2461 | sentTo = SENT_TO_PREVIOUS_LOCATION; | ||
| 2451 | } | 2462 | } |
| 2452 | } | 2463 | } |
| 2453 | } else { | 2464 | } else { |
| @@ -2460,23 +2471,40 @@ void Application::handleSandboxStatus(QNetworkReply* reply) { · 6 unmodified lines | |||
| 2460 | 2471 | ||
| 2461 | // If this is a first run we short-circuit the address passed in | 2472 | // If this is a first run we short-circuit the address passed in |
| 2462 | if (isFirstRun) { | 2473 | if (isFirstRun) { |
| 2463 | if (isUsingHMD) { | 2474 | if (isUsingHMDAndHandControllers) { |
| 2464 | if (sandboxIsRunning) { | 2475 | if (sandboxIsRunning) { |
| 2465 | qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; | 2476 | qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home."; |
| 2466 | DependencyManager::get<AddressManager>()->goToLocalSandbox(); | 2477 | DependencyManager::get<AddressManager>()->goToLocalSandbox(); |
| 2478 | sentTo = SENT_TO_SANDBOX; | ||
| 2467 | } else { | 2479 | } else { |
| 2468 | qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; | 2480 | qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry."; |
| 2469 | DependencyManager::get<AddressManager>()->goToEntry(); | 2481 | DependencyManager::get<AddressManager>()->goToEntry(); |
| 2482 | sentTo = SENT_TO_ENTRY; | ||
| 2470 | } | 2483 | } |
| 2471 | } else { | 2484 | } else { |
| 2472 | DependencyManager::get<AddressManager>()->goToEntry(); | 2485 | DependencyManager::get<AddressManager>()->goToEntry(); |
| 2486 | sentTo = SENT_TO_ENTRY; | ||
| 2473 | } | 2487 | } |
| 2474 | } else { | 2488 | } else { |
| 2475 | qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); | 2489 | qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString); |
| 2476 | DependencyManager::get<AddressManager>()->loadSettings(addressLookupString); | 2490 | DependencyManager::get<AddressManager>()->loadSettings(addressLookupString); |
| 2491 | sentTo = SENT_TO_PREVIOUS_LOCATION; | ||
| 2477 | } | 2492 | } |
| 2478 | } | 2493 | } |
| 2479 | 2494 | ||
| 2495 | UserActivityLogger::getInstance().logAction("startup_sent_to", { | ||
| 2496 | { "sent_to", sentTo }, | ||
| 2497 | { "sandbox_is_running", sandboxIsRunning }, | ||
| 2498 | { "has_hmd", hasHMD }, | ||
| 2499 | { "has_hand_controllers", hasHandControllers }, | ||
| 2500 | { "is_using_hmd", isUsingHMD }, | ||
| 2501 | { "is_using_hmd_and_hand_controllers", isUsingHMDAndHandControllers }, | ||
| 2502 | { "content_version", contentVersion }, | ||
| 2503 | { "is_tutorial_complete", isTutorialComplete }, | ||
| 2504 | { "has_tutorial_content", hasTutorialContent }, | ||
| 2505 | { "should_go_to_tutorial", shouldGoToTutorial } | ||
| 2506 | }); | ||
| 2507 | |||
| 2480 | _connectionMonitor.init(); | 2508 | _connectionMonitor.init(); |
| 2481 | 2509 | ||
| 2482 | // After all of the constructor is completed, then set firstRun to false. | 2510 | // After all of the constructor is completed, then set firstRun to false. |
| @@ -407,6 +407,12 @@ Menu::Menu() { · 406 unmodified lines | |||
| 407 | #endif | 407 | #endif |
| 408 | 408 | ||
| 409 | 409 | ||
| 410 | { | ||
| 411 | auto action = addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderClearKtxCache); | ||
| 412 | connect(action, &QAction::triggered, []{ | ||
| 413 | Setting::Handle<int>(KTXCache::SETTING_VERSION_NAME, KTXCache::INVALID_VERSION).set(KTXCache::INVALID_VERSION); | ||
| 414 | }); | ||
| 415 | } | ||
| 410 | 416 | ||
| 411 | // Developer > Render > LOD Tools | 417 | // Developer > Render > LOD Tools |
| 412 | addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0, | 418 | addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0, |
| @@ -145,6 +145,7 @@ namespace MenuOption { · 144 unmodified lines | |||
| 145 | const QString Quit = "Quit"; | 145 | const QString Quit = "Quit"; |
| 146 | const QString ReloadAllScripts = "Reload All Scripts"; | 146 | const QString ReloadAllScripts = "Reload All Scripts"; |
| 147 | const QString ReloadContent = "Reload Content (Clears all caches)"; | 147 | const QString ReloadContent = "Reload Content (Clears all caches)"; |
| 148 | const QString RenderClearKtxCache = "Clear KTX Cache (requires restart)"; | ||
| 148 | const QString RenderMaxTextureMemory = "Maximum Texture Memory"; | 149 | const QString RenderMaxTextureMemory = "Maximum Texture Memory"; |
| 149 | const QString RenderMaxTextureAutomatic = "Automatic Texture Memory"; | 150 | const QString RenderMaxTextureAutomatic = "Automatic Texture Memory"; |
| 150 | const QString RenderMaxTexture4MB = "4 MB"; | 151 | const QString RenderMaxTexture4MB = "4 MB"; |
| @@ -681,7 +681,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef · 680 unmodified lines | |||
| 681 | // and pretend that we own it (we assume we'll recover it soon) | 681 | // and pretend that we own it (we assume we'll recover it soon) |
| 682 | 682 | ||
| 683 | // However, for now, when the server uses a newer time than what we sent, listen to what we're told. | 683 | // However, for now, when the server uses a newer time than what we sent, listen to what we're told. |
| 684 | if (overwriteLocalData) weOwnSimulation = false; | 684 | if (overwriteLocalData) { |
| 685 | weOwnSimulation = false; | ||
| 686 | } | ||
| 685 | } else if (_simulationOwner.set(newSimOwner)) { | 687 | } else if (_simulationOwner.set(newSimOwner)) { |
| 686 | markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID); | 688 | markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID); |
| 687 | somethingChanged = true; | 689 | somethingChanged = true; |
| @@ -1293,27 +1295,15 @@ void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) c · 605 unmodified lines | |||
| 1293 | properties._accelerationChanged = true; | 1295 | properties._accelerationChanged = true; |
| 1294 | } | 1296 | } |
| 1295 | 1297 | ||
| 1296 | void EntityItem::pokeSimulationOwnership() { | 1298 | void EntityItem::flagForOwnershipBid(uint8_t priority) { |
| 1297 | markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_POKE); | 1299 | markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY); |
| 1298 | auto nodeList = DependencyManager::get<NodeList>(); | ||
| 1299 | if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) { | ||
| 1300 | // we already own it | ||
| 1301 | _simulationOwner.promotePriority(SCRIPT_POKE_SIMULATION_PRIORITY); | ||
| 1302 | } else { | ||
| 1303 | // we don't own it yet | ||
| 1304 | _simulationOwner.setPendingPriority(SCRIPT_POKE_SIMULATION_PRIORITY, usecTimestampNow()); | ||
| 1305 | } | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | void EntityItem::grabSimulationOwnership() { | ||
| 1309 | markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB); | ||
| 1310 | auto nodeList = DependencyManager::get<NodeList>(); | 1300 | auto nodeList = DependencyManager::get<NodeList>(); |
| 1311 | if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) { | 1301 | if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) { |
| 1312 | // we already own it | 1302 | // we already own it |
| 1313 | _simulationOwner.promotePriority(SCRIPT_GRAB_SIMULATION_PRIORITY); | 1303 | _simulationOwner.promotePriority(priority); |
| 1314 | } else { | 1304 | } else { |
| 1315 | // we don't own it yet | 1305 | // we don't own it yet |
| 1316 | _simulationOwner.setPendingPriority(SCRIPT_GRAB_SIMULATION_PRIORITY, usecTimestampNow()); | 1306 | _simulationOwner.setPendingPriority(priority, usecTimestampNow()); |
| 1317 | } | 1307 | } |
| 1318 | } | 1308 | } |
| 1319 | 1309 | ||
| @@ -321,6 +321,7 @@ public: · 320 unmodified lines | |||
| 321 | void updateSimulationOwner(const SimulationOwner& owner); | 321 | void updateSimulationOwner(const SimulationOwner& owner); |
| 322 | void clearSimulationOwnership(); | 322 | void clearSimulationOwnership(); |
| 323 | void setPendingOwnershipPriority(quint8 priority, const quint64& timestamp); | 323 | void setPendingOwnershipPriority(quint8 priority, const quint64& timestamp); |
| 324 | uint8_t getPendingOwnershipPriority() const { return _simulationOwner.getPendingPriority(); } | ||
| 324 | void rememberHasSimulationOwnershipBid() const; | 325 | void rememberHasSimulationOwnershipBid() const; |
| 325 | 326 | ||
| 326 | QString getMarketplaceID() const; | 327 | QString getMarketplaceID() const; |
| @@ -394,8 +395,7 @@ public: · 67 unmodified lines | |||
| 394 | 395 | ||
| 395 | void getAllTerseUpdateProperties(EntityItemProperties& properties) const; | 396 | void getAllTerseUpdateProperties(EntityItemProperties& properties) const; |
| 396 | 397 | ||
| 397 | void pokeSimulationOwnership(); | 398 | void flagForOwnershipBid(uint8_t priority); |
| 398 | void grabSimulationOwnership(); | ||
| 399 | void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } | 399 | void flagForMotionStateChange() { _dirtyFlags |= Simulation::DIRTY_MOTION_TYPE; } |
| 400 | 400 | ||
| 401 | QString actionsToDebugString(); | 401 | QString actionsToDebugString(); |
| @@ -230,6 +230,8 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties · 229 unmodified lines | |||
| 230 | } | 230 | } |
| 231 | 231 | ||
| 232 | entity->setLastBroadcast(usecTimestampNow()); | 232 | entity->setLastBroadcast(usecTimestampNow()); |
| 233 | // since we're creating this object we will immediately volunteer to own its simulation | ||
| 234 | entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY); | ||
| 233 | propertiesWithSimID.setLastEdited(entity->getLastEdited()); | 235 | propertiesWithSimID.setLastEdited(entity->getLastEdited()); |
| 234 | } else { | 236 | } else { |
| 235 | qCDebug(entities) << "script failed to add new Entity to local Octree"; | 237 | qCDebug(entities) << "script failed to add new Entity to local Octree"; |
| @@ -440,7 +442,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& · 204 unmodified lines | |||
| 440 | } else { | 442 | } else { |
| 441 | // we make a bid for simulation ownership | 443 | // we make a bid for simulation ownership |
| 442 | properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY); | 444 | properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY); |
| 443 | entity->pokeSimulationOwnership(); | 445 | entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY); |
| 444 | entity->rememberHasSimulationOwnershipBid(); | 446 | entity->rememberHasSimulationOwnershipBid(); |
| 445 | } | 447 | } |
| 446 | } | 448 | } |
| @@ -1194,7 +1196,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, · 747 unmodified lines | |||
| 1194 | } | 1196 | } |
| 1195 | action->setIsMine(true); | 1197 | action->setIsMine(true); |
| 1196 | success = entity->addAction(simulation, action); | 1198 | success = entity->addAction(simulation, action); |
| 1197 | entity->grabSimulationOwnership(); | 1199 | entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY); |
| 1198 | return false; // Physics will cause a packet to be sent, so don't send from here. | 1200 | return false; // Physics will cause a packet to be sent, so don't send from here. |
| 1199 | }); | 1201 | }); |
| 1200 | if (success) { | 1202 | if (success) { |
| @@ -1210,7 +1212,7 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid& · 9 unmodified lines | |||
| 1210 | return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) { | 1212 | return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) { |
| 1211 | bool success = entity->updateAction(simulation, actionID, arguments); | 1213 | bool success = entity->updateAction(simulation, actionID, arguments); |
| 1212 | if (success) { | 1214 | if (success) { |
| 1213 | entity->grabSimulationOwnership(); | 1215 | entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY); |
| 1214 | } | 1216 | } |
| 1215 | return success; | 1217 | return success; |
| 1216 | }); | 1218 | }); |
| @@ -1224,7 +1226,7 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid& · 7 unmodified lines | |||
| 1224 | success = entity->removeAction(simulation, actionID); | 1226 | success = entity->removeAction(simulation, actionID); |
| 1225 | if (success) { | 1227 | if (success) { |
| 1226 | // reduce from grab to poke | 1228 | // reduce from grab to poke |
| 1227 | entity->pokeSimulationOwnership(); | 1229 | entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY); |
| 1228 | } | 1230 | } |
| 1229 | return false; // Physics will cause a packet to be sent, so don't send from here. | 1231 | return false; // Physics will cause a packet to be sent, so don't send from here. |
| 1230 | }); | 1232 | }); |
| @@ -26,12 +26,10 @@ namespace Simulation { · 25 unmodified lines | |||
| 26 | const uint32_t DIRTY_MATERIAL = 0x00400; | 26 | const uint32_t DIRTY_MATERIAL = 0x00400; |
| 27 | const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine | 27 | const uint32_t DIRTY_PHYSICS_ACTIVATION = 0x0800; // should activate object in physics engine |
| 28 | const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed | 28 | const uint32_t DIRTY_SIMULATOR_ID = 0x1000; // the simulatorID has changed |
| 29 | const uint32_t DIRTY_SIMULATION_OWNERSHIP_FOR_POKE = 0x2000; // bid for simulation ownership at "poke" | 29 | const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = 0x2000; // our own bid priority has changed |
| 30 | const uint32_t DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB = 0x4000; // bid for simulation ownership at "grab" | ||
| 31 | 30 | ||
| 32 | const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; | 31 | const uint32_t DIRTY_TRANSFORM = DIRTY_POSITION | DIRTY_ROTATION; |
| 33 | const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; | 32 | const uint32_t DIRTY_VELOCITIES = DIRTY_LINEAR_VELOCITY | DIRTY_ANGULAR_VELOCITY; |
| 34 | const uint32_t DIRTY_SIMULATION_OWNERSHIP_PRIORITY = DIRTY_SIMULATION_OWNERSHIP_FOR_POKE | DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB; | ||
| 35 | }; | 33 | }; |
| 36 | 34 | ||
| 37 | #endif // hifi_SimulationFlags_h | 35 | #endif // hifi_SimulationFlags_h |
| @@ -26,9 +26,9 @@ const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1; · 25 unmodified lines | |||
| 26 | SimulationOwner::SimulationOwner() : | 26 | SimulationOwner::SimulationOwner() : |
| 27 | _id(), | 27 | _id(), |
| 28 | _expiry(0), | 28 | _expiry(0), |
| 29 | _pendingTimestamp(0), | 29 | _pendingBidTimestamp(0), |
| 30 | _priority(0), | 30 | _priority(0), |
| 31 | _pendingPriority(0), | 31 | _pendingBidPriority(0), |
| 32 | _pendingState(PENDING_STATE_NOTHING) | 32 | _pendingState(PENDING_STATE_NOTHING) |
| 33 | { | 33 | { |
| 34 | } | 34 | } |
| @@ -36,9 +36,9 @@ SimulationOwner::SimulationOwner() : · 1 unmodified line | |||
| 36 | SimulationOwner::SimulationOwner(const QUuid& id, quint8 priority) : | 36 | SimulationOwner::SimulationOwner(const QUuid& id, quint8 priority) : |
| 37 | _id(id), | 37 | _id(id), |
| 38 | _expiry(0), | 38 | _expiry(0), |
| 39 | _pendingTimestamp(0), | 39 | _pendingBidTimestamp(0), |
| 40 | _priority(priority), | 40 | _priority(priority), |
| 41 | _pendingPriority(0) | 41 | _pendingBidPriority(0) |
| 42 | { | 42 | { |
| 43 | } | 43 | } |
| 44 | 44 | ||
| @@ -61,9 +61,9 @@ bool SimulationOwner::fromByteArray(const QByteArray& data) { · 16 unmodified lines | |||
| 61 | void SimulationOwner::clear() { | 61 | void SimulationOwner::clear() { |
| 62 | _id = QUuid(); | 62 | _id = QUuid(); |
| 63 | _expiry = 0; | 63 | _expiry = 0; |
| 64 | _pendingTimestamp = 0; | 64 | _pendingBidTimestamp = 0; |
| 65 | _priority = 0; | 65 | _priority = 0; |
| 66 | _pendingPriority = 0; | 66 | _pendingBidPriority = 0; |
| 67 | _pendingState = PENDING_STATE_NOTHING; | 67 | _pendingState = PENDING_STATE_NOTHING; |
| 68 | } | 68 | } |
| 69 | 69 | ||
| @@ -102,9 +102,9 @@ bool SimulationOwner::set(const SimulationOwner& owner) { · 32 unmodified lines | |||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | void SimulationOwner::setPendingPriority(quint8 priority, const quint64& timestamp) { | 104 | void SimulationOwner::setPendingPriority(quint8 priority, const quint64& timestamp) { |
| 105 | _pendingPriority = priority; | 105 | _pendingBidPriority = priority; |
| 106 | _pendingTimestamp = timestamp; | 106 | _pendingBidTimestamp = timestamp; |
| 107 | _pendingState = (_pendingPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE; | 107 | _pendingState = (_pendingBidPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE; |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | void SimulationOwner::updateExpiry() { | 110 | void SimulationOwner::updateExpiry() { |
| @@ -113,11 +113,11 @@ void SimulationOwner::updateExpiry() { · 2 unmodified lines | |||
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | bool SimulationOwner::pendingRelease(const quint64& timestamp) { | 115 | bool SimulationOwner::pendingRelease(const quint64& timestamp) { |
| 116 | return _pendingPriority == 0 && _pendingState == PENDING_STATE_RELEASE && _pendingTimestamp >= timestamp; | 116 | return _pendingBidPriority == 0 && _pendingState == PENDING_STATE_RELEASE && _pendingBidTimestamp >= timestamp; |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | bool SimulationOwner::pendingTake(const quint64& timestamp) { | 119 | bool SimulationOwner::pendingTake(const quint64& timestamp) { |
| 120 | return _pendingPriority > 0 && _pendingState == PENDING_STATE_TAKE && _pendingTimestamp >= timestamp; | 120 | return _pendingBidPriority > 0 && _pendingState == PENDING_STATE_TAKE && _pendingBidTimestamp >= timestamp; |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | void SimulationOwner::clearCurrentOwner() { | 123 | void SimulationOwner::clearCurrentOwner() { |
| @@ -66,6 +66,7 @@ public: · 65 unmodified lines | |||
| 66 | 66 | ||
| 67 | bool hasExpired() const { return usecTimestampNow() > _expiry; } | 67 | bool hasExpired() const { return usecTimestampNow() > _expiry; } |
| 68 | 68 | ||
| 69 | uint8_t getPendingPriority() const { return _pendingBidPriority; } | ||
| 69 | bool pendingRelease(const quint64& timestamp); // return true if valid pending RELEASE | 70 | bool pendingRelease(const quint64& timestamp); // return true if valid pending RELEASE |
| 70 | bool pendingTake(const quint64& timestamp); // return true if valid pending TAKE | 71 | bool pendingTake(const quint64& timestamp); // return true if valid pending TAKE |
| 71 | void clearCurrentOwner(); | 72 | void clearCurrentOwner(); |
| @@ -84,9 +85,9 @@ public: · 12 unmodified lines | |||
| 84 | private: | 85 | private: |
| 85 | QUuid _id; // owner | 86 | QUuid _id; // owner |
| 86 | quint64 _expiry; // time when ownership can transition at equal priority | 87 | quint64 _expiry; // time when ownership can transition at equal priority |
| 87 | quint64 _pendingTimestamp; // time when pending update was set | 88 | quint64 _pendingBidTimestamp; // time when pending bid was set |
| 88 | quint8 _priority; // priority of current owner | 89 | quint8 _priority; // priority of current owner |
| 89 | quint8 _pendingPriority; // priority of pendingTake | 90 | quint8 _pendingBidPriority; // priority at which we'd like to own it |
| 90 | quint8 _pendingState; // NOTHING, TAKE, or RELEASE | 91 | quint8 _pendingState; // NOTHING, TAKE, or RELEASE |
| 91 | }; | 92 | }; |
| 92 | 93 | ||
| @@ -11,14 +11,28 @@ · 10 unmodified lines | |||
| 11 | 11 | ||
| 12 | #include "KTXCache.h" | 12 | #include "KTXCache.h" |
| 13 | 13 | ||
| 14 | #include <SettingHandle.h> | ||
| 14 | #include <ktx/KTX.h> | 15 | #include <ktx/KTX.h> |
| 15 | 16 | ||
| 16 | using File = cache::File; | 17 | using File = cache::File; |
| 17 | using FilePointer = cache::FilePointer; | 18 | using FilePointer = cache::FilePointer; |
| 18 | 19 | ||
| 20 | // Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible, | ||
| 21 | // this value should be incremented. This will force the KTX cache to be wiped | ||
| 22 | const int KTXCache::CURRENT_VERSION = 0x01; | ||
| 23 | const int KTXCache::INVALID_VERSION = 0x00; | ||
| 24 | const char* KTXCache::SETTING_VERSION_NAME = "hifi.ktx.cache_version"; | ||
| 25 | |||
| 19 | KTXCache::KTXCache(const std::string& dir, const std::string& ext) : | 26 | KTXCache::KTXCache(const std::string& dir, const std::string& ext) : |
| 20 | FileCache(dir, ext) { | 27 | FileCache(dir, ext) { |
| 21 | initialize(); | 28 | initialize(); |
| 29 | |||
| 30 | Setting::Handle<int> cacheVersionHandle(SETTING_VERSION_NAME, INVALID_VERSION); | ||
| 31 | auto cacheVersion = cacheVersionHandle.get(); | ||
| 32 | if (cacheVersion != CURRENT_VERSION) { | ||
| 33 | wipe(); | ||
| 34 | cacheVersionHandle.set(CURRENT_VERSION); | ||
| 35 | } | ||
| 22 | } | 36 | } |
| 23 | 37 | ||
| 24 | KTXFilePointer KTXCache::writeFile(const char* data, Metadata&& metadata) { | 38 | KTXFilePointer KTXCache::writeFile(const char* data, Metadata&& metadata) { |
| @@ -27,6 +27,12 @@ class KTXCache : public cache::FileCache { · 26 unmodified lines | |||
| 27 | Q_OBJECT | 27 | Q_OBJECT |
| 28 | 28 | ||
| 29 | public: | 29 | public: |
| 30 | // Whenever a change is made to the serialized format for the KTX cache that isn't backward compatible, | ||
| 31 | // this value should be incremented. This will force the KTX cache to be wiped | ||
| 32 | static const int CURRENT_VERSION; | ||
| 33 | static const int INVALID_VERSION; | ||
| 34 | static const char* SETTING_VERSION_NAME; | ||
| 35 | |||
| 30 | KTXCache(const std::string& dir, const std::string& ext); | 36 | KTXCache(const std::string& dir, const std::string& ext); |
| 31 | 37 | ||
| 32 | KTXFilePointer writeFile(const char* data, Metadata&& metadata); | 38 | KTXFilePointer writeFile(const char* data, Metadata&& metadata); |
| @@ -236,6 +236,28 @@ namespace cache { · 235 unmodified lines | |||
| 236 | }; | 236 | }; |
| 237 | } | 237 | } |
| 238 | 238 | ||
| 239 | void FileCache::eject(const FilePointer& file) { | ||
| 240 | file->_cache = nullptr; | ||
| 241 | const auto& length = file->getLength(); | ||
| 242 | const auto& key = file->getKey(); | ||
| 243 | |||
| 244 | { | ||
| 245 | Lock lock(_filesMutex); | ||
| 246 | if (0 != _files.erase(key)) { | ||
| 247 | _numTotalFiles -= 1; | ||
| 248 | _totalFilesSize -= length; | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | { | ||
| 253 | Lock unusedLock(_unusedFilesMutex); | ||
| 254 | if (0 != _unusedFiles.erase(file)) { | ||
| 255 | _numUnusedFiles -= 1; | ||
| 256 | _unusedFilesSize -= length; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 239 | void FileCache::clean() { | 261 | void FileCache::clean() { |
| 240 | size_t overbudgetAmount = getOverbudgetAmount(); | 262 | size_t overbudgetAmount = getOverbudgetAmount(); |
| 241 | 263 | ||
| @@ -250,28 +272,23 @@ void FileCache::clean() { · 8 unmodified lines | |||
| 250 | for (const auto& file : _unusedFiles) { | 272 | for (const auto& file : _unusedFiles) { |
| 251 | queue.push(file); | 273 | queue.push(file); |
| 252 | } | 274 | } |
| 275 | |||
| 253 | while (!queue.empty() && overbudgetAmount > 0) { | 276 | while (!queue.empty() && overbudgetAmount > 0) { |
| 254 | auto file = queue.top(); | 277 | auto file = queue.top(); |
| 255 | queue.pop(); | 278 | queue.pop(); |
| 279 | eject(file); | ||
| 256 | auto length = file->getLength(); | 280 | auto length = file->getLength(); |
| 257 | |||
| 258 | unusedLock.unlock(); | ||
| 259 | { | ||
| 260 | file->_cache = nullptr; | ||
| 261 | Lock lock(_filesMutex); | ||
| 262 | _files.erase(file->getKey()); | ||
| 263 | } | ||
| 264 | unusedLock.lock(); | ||
| 265 | |||
| 266 | _unusedFiles.erase(file); | ||
| 267 | _numTotalFiles -= 1; | ||
| 268 | _numUnusedFiles -= 1; | ||
| 269 | _totalFilesSize -= length; | ||
| 270 | _unusedFilesSize -= length; | ||
| 271 | overbudgetAmount -= std::min(length, overbudgetAmount); | 281 | overbudgetAmount -= std::min(length, overbudgetAmount); |
| 272 | } | 282 | } |
| 273 | } | 283 | } |
| 274 | 284 | ||
| 285 | void FileCache::wipe() { | ||
| 286 | Lock unusedFilesLock(_unusedFilesMutex); | ||
| 287 | while (!_unusedFiles.empty()) { | ||
| 288 | eject(*_unusedFiles.begin()); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 275 | void FileCache::clear() { | 292 | void FileCache::clear() { |
| 276 | // Eliminate any overbudget files | 293 | // Eliminate any overbudget files |
| 277 | clean(); | 294 | clean(); |
| @@ -46,6 +46,9 @@ public: · 45 unmodified lines | |||
| 46 | FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); | 46 | FileCache(const std::string& dirname, const std::string& ext, QObject* parent = nullptr); |
| 47 | virtual ~FileCache(); | 47 | virtual ~FileCache(); |
| 48 | 48 | ||
| 49 | // Remove all unlocked items from the cache | ||
| 50 | void wipe(); | ||
| 51 | |||
| 49 | size_t getNumTotalFiles() const { return _numTotalFiles; } | 52 | size_t getNumTotalFiles() const { return _numTotalFiles; } |
| 50 | size_t getNumCachedFiles() const { return _numUnusedFiles; } | 53 | size_t getNumCachedFiles() const { return _numUnusedFiles; } |
| 51 | size_t getSizeTotalFiles() const { return _totalFilesSize; } | 54 | size_t getSizeTotalFiles() const { return _totalFilesSize; } |
| @@ -95,6 +98,9 @@ public: · 43 unmodified lines | |||
| 95 | private: | 98 | private: |
| 96 | using Mutex = std::recursive_mutex; | 99 | using Mutex = std::recursive_mutex; |
| 97 | using Lock = std::unique_lock<Mutex>; | 100 | using Lock = std::unique_lock<Mutex>; |
| 101 | using Map = std::unordered_map<Key, std::weak_ptr<File>>; | ||
| 102 | using Set = std::unordered_set<FilePointer>; | ||
| 103 | using KeySet = std::unordered_set<Key>; | ||
| 98 | 104 | ||
| 99 | friend class File; | 105 | friend class File; |
| 100 | 106 | ||
| @@ -105,6 +111,8 @@ private: · 4 unmodified lines | |||
| 105 | void removeUnusedFile(const FilePointer& file); | 111 | void removeUnusedFile(const FilePointer& file); |
| 106 | void clean(); | 112 | void clean(); |
| 107 | void clear(); | 113 | void clear(); |
| 114 | // Remove a file from the cache | ||
| 115 | void eject(const FilePointer& file); | ||
| 108 | 116 | ||
| 109 | size_t getOverbudgetAmount() const; | 117 | size_t getOverbudgetAmount() const; |
| 110 | 118 | ||
| @@ -122,10 +130,10 @@ private: · 11 unmodified lines | |||
| 122 | std::string _dirpath; | 130 | std::string _dirpath; |
| 123 | bool _initialized { false }; | 131 | bool _initialized { false }; |
| 124 | 132 | ||
| 125 | std::unordered_map<Key, std::weak_ptr<File>> _files; | 133 | Map _files; |
| 126 | Mutex _filesMutex; | 134 | Mutex _filesMutex; |
| 127 | 135 | ||
| 128 | std::unordered_set<FilePointer> _unusedFiles; | 136 | Set _unusedFiles; |
| 129 | Mutex _unusedFilesMutex; | 137 | Mutex _unusedFilesMutex; |
| 130 | }; | 138 | }; |
| 131 | 139 | ||
| @@ -136,8 +144,8 @@ public: · 4 unmodified lines | |||
| 136 | using Key = FileCache::Key; | 144 | using Key = FileCache::Key; |
| 137 | using Metadata = FileCache::Metadata; | 145 | using Metadata = FileCache::Metadata; |
| 138 | 146 | ||
| 139 | Key getKey() const { return _key; } | 147 | const Key& getKey() const { return _key; } |
| 140 | size_t getLength() const { return _length; } | 148 | const size_t& getLength() const { return _length; } |
| 141 | std::string getFilepath() const { return _filepath; } | 149 | std::string getFilepath() const { return _filepath; } |
| 142 | 150 | ||
| 143 | virtual ~File(); | 151 | virtual ~File(); |
| @@ -65,8 +65,7 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer · 64 unmodified lines | |||
| 65 | _lastStep(0), | 65 | _lastStep(0), |
| 66 | _loopsWithoutOwner(0), | 66 | _loopsWithoutOwner(0), |
| 67 | _accelerationNearlyGravityCount(0), | 67 | _accelerationNearlyGravityCount(0), |
| 68 | _numInactiveUpdates(1), | 68 | _numInactiveUpdates(1) |
| 69 | _outgoingPriority(0) | ||
| 70 | { | 69 | { |
| 71 | _type = MOTIONSTATE_TYPE_ENTITY; | 70 | _type = MOTIONSTATE_TYPE_ENTITY; |
| 72 | assert(_entity); | 71 | assert(_entity); |
| @@ -75,6 +74,8 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer · 2 unmodified lines | |||
| 75 | // we need the side-effects of EntityMotionState::setShape() so we call it explicitly here | 74 | // we need the side-effects of EntityMotionState::setShape() so we call it explicitly here |
| 76 | // rather than pass the legit shape pointer to the ObjectMotionState ctor above. | 75 | // rather than pass the legit shape pointer to the ObjectMotionState ctor above. |
| 77 | setShape(shape); | 76 | setShape(shape); |
| 77 | |||
| 78 | _outgoingPriority = _entity->getPendingOwnershipPriority(); | ||
| 78 | } | 79 | } |
| 79 | 80 | ||
| 80 | EntityMotionState::~EntityMotionState() { | 81 | EntityMotionState::~EntityMotionState() { |
| @@ -84,7 +85,7 @@ EntityMotionState::~EntityMotionState() { · 3 unmodified lines | |||
| 84 | 85 | ||
| 85 | void EntityMotionState::updateServerPhysicsVariables() { | 86 | void EntityMotionState::updateServerPhysicsVariables() { |
| 86 | assert(entityTreeIsLocked()); | 87 | assert(entityTreeIsLocked()); |
| 87 | if (_entity->getSimulatorID() == Physics::getSessionUUID()) { | 88 | if (isLocallyOwned()) { |
| 88 | // don't slam these values if we are the simulation owner | 89 | // don't slam these values if we are the simulation owner |
| 89 | return; | 90 | return; |
| 90 | } | 91 | } |
| @@ -136,23 +137,23 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) { · 45 unmodified lines | |||
| 136 | _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; | 137 | _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; |
| 137 | } | 138 | } |
| 138 | _loopsWithoutOwner = 0; | 139 | _loopsWithoutOwner = 0; |
| 139 | } else if (_entity->getSimulatorID() == Physics::getSessionUUID()) { | 140 | _numInactiveUpdates = 0; |
| 141 | } else if (isLocallyOwned()) { | ||
| 140 | // we just inherited ownership, make sure our desired priority matches what we have | 142 | // we just inherited ownership, make sure our desired priority matches what we have |
| 141 | upgradeOutgoingPriority(_entity->getSimulationPriority()); | 143 | upgradeOutgoingPriority(_entity->getSimulationPriority()); |
| 142 | } else { | 144 | } else { |
| 143 | _outgoingPriority = 0; | 145 | _outgoingPriority = 0; |
| 144 | _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; | 146 | _nextOwnershipBid = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS; |
| 147 | _numInactiveUpdates = 0; | ||
| 145 | } | 148 | } |
| 146 | } | 149 | } |
| 147 | if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY) { | 150 | if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY) { |
| 148 | // The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bits really mean "we should bid for ownership because | 151 | // The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bit means one of the following: |
| 149 | // a local script has been changing physics properties, or we should adjust our own ownership priority". | 152 | // (1) we own it but may need to change the priority OR... |
| 150 | // The desired priority is determined by which bits were set. | 153 | // (2) we don't own it but should bid (because a local script has been changing physics properties) |
| 151 | if (flags & Simulation::DIRTY_SIMULATION_OWNERSHIP_FOR_GRAB) { | 154 | uint8_t newPriority = isLocallyOwned() ? _entity->getSimulationOwner().getPriority() : _entity->getSimulationOwner().getPendingPriority(); |
| 152 | _outgoingPriority = SCRIPT_GRAB_SIMULATION_PRIORITY; | 155 | _outgoingPriority = glm::max(_outgoingPriority, newPriority); |
| 153 | } else { | 156 | |
| 154 | _outgoingPriority = SCRIPT_POKE_SIMULATION_PRIORITY; | ||
| 155 | } | ||
| 156 | // reset bid expiry so that we bid ASAP | 157 | // reset bid expiry so that we bid ASAP |
| 157 | _nextOwnershipBid = 0; | 158 | _nextOwnershipBid = 0; |
| 158 | } | 159 | } |
| @@ -317,7 +318,7 @@ bool EntityMotionState::isCandidateForOwnership() const { · 158 unmodified lines | |||
| 317 | assert(_entity); | 318 | assert(_entity); |
| 318 | assert(entityTreeIsLocked()); | 319 | assert(entityTreeIsLocked()); |
| 319 | return _outgoingPriority != 0 | 320 | return _outgoingPriority != 0 |
| 320 | || Physics::getSessionUUID() == _entity->getSimulatorID() | 321 | || isLocallyOwned() |
| 321 | || _entity->dynamicDataNeedsTransmit(); | 322 | || _entity->dynamicDataNeedsTransmit(); |
| 322 | } | 323 | } |
| 323 | 324 | ||
| @@ -491,7 +492,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { · 167 unmodified lines | |||
| 491 | return true; | 492 | return true; |
| 492 | } | 493 | } |
| 493 | 494 | ||
| 494 | if (_entity->getSimulatorID() != Physics::getSessionUUID()) { | 495 | if (!isLocallyOwned()) { |
| 495 | // we don't own the simulation | 496 | // we don't own the simulation |
| 496 | 497 | ||
| 497 | // NOTE: we do not volunteer to own kinematic or static objects | 498 | // NOTE: we do not volunteer to own kinematic or static objects |
| @@ -599,7 +600,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ · 101 unmodified lines | |||
| 599 | properties.clearSimulationOwner(); | 600 | properties.clearSimulationOwner(); |
| 600 | _outgoingPriority = 0; | 601 | _outgoingPriority = 0; |
| 601 | _entity->setPendingOwnershipPriority(_outgoingPriority, now); | 602 | _entity->setPendingOwnershipPriority(_outgoingPriority, now); |
| 602 | } else if (Physics::getSessionUUID() != _entity->getSimulatorID()) { | 603 | } else if (!isLocallyOwned()) { |
| 603 | // we don't own the simulation for this entity yet, but we're sending a bid for it | 604 | // we don't own the simulation for this entity yet, but we're sending a bid for it |
| 604 | quint8 bidPriority = glm::max<uint8_t>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY); | 605 | quint8 bidPriority = glm::max<uint8_t>(_outgoingPriority, VOLUNTEER_SIMULATION_PRIORITY); |
| 605 | properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority); | 606 | properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority); |
| @@ -788,6 +789,10 @@ void EntityMotionState::computeCollisionGroupAndMask(int16_t& group, int16_t& ma · 182 unmodified lines | |||
| 788 | _entity->computeCollisionGroupAndFinalMask(group, mask); | 789 | _entity->computeCollisionGroupAndFinalMask(group, mask); |
| 789 | } | 790 | } |
| 790 | 791 | ||
| 792 | bool EntityMotionState::isLocallyOwned() const { | ||
| 793 | return _entity->getSimulatorID() == Physics::getSessionUUID(); | ||
| 794 | } | ||
| 795 | |||
| 791 | bool EntityMotionState::shouldBeLocallyOwned() const { | 796 | bool EntityMotionState::shouldBeLocallyOwned() const { |
| 792 | return (_outgoingPriority > VOLUNTEER_SIMULATION_PRIORITY && _outgoingPriority > _entity->getSimulationPriority()) || | 797 | return (_outgoingPriority > VOLUNTEER_SIMULATION_PRIORITY && _outgoingPriority > _entity->getSimulationPriority()) || |
| 793 | _entity->getSimulatorID() == Physics::getSessionUUID(); | 798 | _entity->getSimulatorID() == Physics::getSessionUUID(); |
| @@ -79,6 +79,7 @@ public: · 78 unmodified lines | |||
| 79 | 79 | ||
| 80 | virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override; | 80 | virtual void computeCollisionGroupAndMask(int16_t& group, int16_t& mask) const override; |
| 81 | 81 | ||
| 82 | bool isLocallyOwned() const override; | ||
| 82 | bool shouldBeLocallyOwned() const override; | 83 | bool shouldBeLocallyOwned() const override; |
| 83 | 84 | ||
| 84 | friend class PhysicalEntitySimulation; | 85 | friend class PhysicalEntitySimulation; |
| @@ -146,6 +146,7 @@ public: · 145 unmodified lines | |||
| 146 | void dirtyInternalKinematicChanges() { _hasInternalKinematicChanges = true; } | 146 | void dirtyInternalKinematicChanges() { _hasInternalKinematicChanges = true; } |
| 147 | void clearInternalKinematicChanges() { _hasInternalKinematicChanges = false; } | 147 | void clearInternalKinematicChanges() { _hasInternalKinematicChanges = false; } |
| 148 | 148 | ||
| 149 | virtual bool isLocallyOwned() const { return false; } | ||
| 149 | virtual bool shouldBeLocallyOwned() const { return false; } | 150 | virtual bool shouldBeLocallyOwned() const { return false; } |
| 150 | 151 | ||
| 151 | friend class PhysicsEngine; | 152 | friend class PhysicsEngine; |
| @@ -129,6 +129,9 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { · 128 unmodified lines | |||
| 129 | } | 129 | } |
| 130 | body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); | 130 | body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); |
| 131 | body->updateInertiaTensor(); | 131 | body->updateInertiaTensor(); |
| 132 | if (motionState->isLocallyOwned()) { | ||
| 133 | _activeStaticBodies.insert(body); | ||
| 134 | } | ||
| 132 | break; | 135 | break; |
| 133 | } | 136 | } |
| 134 | } | 137 | } |
| @@ -174,19 +177,9 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) { · 39 unmodified lines | |||
| 174 | // frame (because the framerate is faster than our physics simulation rate). When this happens we must scan | 177 | // frame (because the framerate is faster than our physics simulation rate). When this happens we must scan |
| 175 | // _activeStaticBodies for objects that were recently deleted so we don't try to access a dangling pointer. | 178 | // _activeStaticBodies for objects that were recently deleted so we don't try to access a dangling pointer. |
| 176 | for (auto object : objects) { | 179 | for (auto object : objects) { |
| 177 | btRigidBody* body = object->getRigidBody(); | 180 | std::set<btRigidBody*>::iterator itr = _activeStaticBodies.find(object->getRigidBody()); |
| 178 | 181 | if (itr != _activeStaticBodies.end()) { | |
| 179 | std::vector<btRigidBody*>::reverse_iterator itr = _activeStaticBodies.rbegin(); | 182 | _activeStaticBodies.erase(itr); |
| 180 | while (itr != _activeStaticBodies.rend()) { | ||
| 181 | if (body == *itr) { | ||
| 182 | if (*itr != *(_activeStaticBodies.rbegin())) { | ||
| 183 | // swap with rbegin | ||
| 184 | *itr = *(_activeStaticBodies.rbegin()); | ||
| 185 | } | ||
| 186 | _activeStaticBodies.pop_back(); | ||
| 187 | break; | ||
| 188 | } | ||
| 189 | ++itr; | ||
| 190 | } | 183 | } |
| 191 | } | 184 | } |
| 192 | } | 185 | } |
| @@ -245,14 +238,16 @@ VectorOfMotionStates PhysicsEngine::changeObjects(const VectorOfMotionStates& ob · 52 unmodified lines | |||
| 245 | object->clearIncomingDirtyFlags(); | 238 | object->clearIncomingDirtyFlags(); |
| 246 | } | 239 | } |
| 247 | if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) { | 240 | if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) { |
| 248 | _activeStaticBodies.push_back(object->getRigidBody()); | 241 | _activeStaticBodies.insert(object->getRigidBody()); |
| 249 | } | 242 | } |
| 250 | } | 243 | } |
| 251 | // active static bodies have changed (in an Easy way) and need their Aabbs updated | 244 | // active static bodies have changed (in an Easy way) and need their Aabbs updated |
| 252 | // but we've configured Bullet to NOT update them automatically (for improved performance) | 245 | // but we've configured Bullet to NOT update them automatically (for improved performance) |
| 253 | // so we must do it ourselves | 246 | // so we must do it ourselves |
| 254 | for (size_t i = 0; i < _activeStaticBodies.size(); ++i) { | 247 | std::set<btRigidBody*>::const_iterator itr = _activeStaticBodies.begin(); |
| 255 | _dynamicsWorld->updateSingleAabb(_activeStaticBodies[i]); | 248 | while (itr != _activeStaticBodies.end()) { |
| 249 | _dynamicsWorld->updateSingleAabb(*itr); | ||
| 250 | ++itr; | ||
| 256 | } | 251 | } |
| 257 | return stillNeedChange; | 252 | return stillNeedChange; |
| 258 | } | 253 | } |
| @@ -496,13 +491,23 @@ const CollisionEvents& PhysicsEngine::getCollisionEvents() { · 237 unmodified lines | |||
| 496 | 491 | ||
| 497 | const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() { | 492 | const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() { |
| 498 | BT_PROFILE("copyOutgoingChanges"); | 493 | BT_PROFILE("copyOutgoingChanges"); |
| 494 | |||
| 495 | _dynamicsWorld->synchronizeMotionStates(); | ||
| 496 | |||
| 499 | // Bullet will not deactivate static objects (it doesn't expect them to be active) | 497 | // Bullet will not deactivate static objects (it doesn't expect them to be active) |
| 500 | // so we must deactivate them ourselves | 498 | // so we must deactivate them ourselves |
| 501 | for (size_t i = 0; i < _activeStaticBodies.size(); ++i) { | 499 | std::set<btRigidBody*>::const_iterator itr = _activeStaticBodies.begin(); |
| 502 | _activeStaticBodies[i]->forceActivationState(ISLAND_SLEEPING); | 500 | while (itr != _activeStaticBodies.end()) { |
| 501 | btRigidBody* body = *itr; | ||
| 502 | body->forceActivationState(ISLAND_SLEEPING); | ||
| 503 | ObjectMotionState* motionState = static_cast<ObjectMotionState*>(body->getUserPointer()); | ||
| 504 | if (motionState) { | ||
| 505 | _dynamicsWorld->addChangedMotionState(motionState); | ||
| 506 | } | ||
| 507 | ++itr; | ||
| 503 | } | 508 | } |
| 504 | _activeStaticBodies.clear(); | 509 | _activeStaticBodies.clear(); |
| 505 | _dynamicsWorld->synchronizeMotionStates(); | 510 | |
| 506 | _hasOutgoingChanges = false; | 511 | _hasOutgoingChanges = false; |
| 507 | return _dynamicsWorld->getChangedMotionStates(); | 512 | return _dynamicsWorld->getChangedMotionStates(); |
| 508 | } | 513 | } |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | |||
| 13 | #define hifi_PhysicsEngine_h | 13 | #define hifi_PhysicsEngine_h |
| 14 | 14 | ||
| 15 | #include <stdint.h> | 15 | #include <stdint.h> |
| 16 | #include <set> | ||
| 16 | #include <vector> | 17 | #include <vector> |
| 17 | 18 | ||
| 18 | #include <QUuid> | 19 | #include <QUuid> |
| @@ -114,7 +115,7 @@ private: · 95 unmodified lines | |||
| 114 | CollisionEvents _collisionEvents; | 115 | CollisionEvents _collisionEvents; |
| 115 | QHash<QUuid, EntityDynamicPointer> _objectDynamics; | 116 | QHash<QUuid, EntityDynamicPointer> _objectDynamics; |
| 116 | QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody; | 117 | QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody; |
| 117 | std::vector<btRigidBody*> _activeStaticBodies; | 118 | std::set<btRigidBody*> _activeStaticBodies; |
| 118 | 119 | ||
| 119 | glm::vec3 _originOffset; | 120 | glm::vec3 _originOffset; |
| 120 | 121 | ||
| @@ -51,6 +51,8 @@ public: · 50 unmodified lines | |||
| 51 | const VectorOfMotionStates& getChangedMotionStates() const { return _changedMotionStates; } | 51 | const VectorOfMotionStates& getChangedMotionStates() const { return _changedMotionStates; } |
| 52 | const VectorOfMotionStates& getDeactivatedMotionStates() const { return _deactivatedStates; } | 52 | const VectorOfMotionStates& getDeactivatedMotionStates() const { return _deactivatedStates; } |
| 53 | 53 | ||
| 54 | void addChangedMotionState(ObjectMotionState* motionState) { _changedMotionStates.push_back(motionState); } | ||
| 55 | |||
| 54 | private: | 56 | private: |
| 55 | // call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState() | 57 | // call this instead of non-virtual btDiscreteDynamicsWorld::synchronizeSingleMotionState() |
| 56 | void synchronizeMotionState(btRigidBody* body); | 58 | void synchronizeMotionState(btRigidBody* body); |
| @@ -113,18 +113,21 @@ void FileCacheTests::testUnusedFiles() { · 112 unmodified lines | |||
| 113 | QVERIFY(!file.get()); | 113 | QVERIFY(!file.get()); |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | QThread::msleep(1000); | ||
| 117 | // Test files 90 to 99 are present | 116 | // Test files 90 to 99 are present |
| 118 | for (int i = 90; i < 100; ++i) { | 117 | for (int i = 90; i < 100; ++i) { |
| 119 | std::string key = getFileKey(i); | 118 | std::string key = getFileKey(i); |
| 120 | auto file = cache->getFile(key); | 119 | auto file = cache->getFile(key); |
| 121 | QVERIFY(file.get()); | 120 | QVERIFY(file.get()); |
| 122 | inUseFiles.push_back(file); | 121 | inUseFiles.push_back(file); |
| 123 | // Each access touches the file, so we need to sleep here to ensure that the files are | 122 | |
| 124 | // spaced out in numeric order, otherwise later tests can't reliably determine the order | 123 | if (i == 94) { |
| 125 | // for cache ejection | 124 | // Each access touches the file, so we need to sleep here to ensure that the the last 5 files |
| 126 | QThread::msleep(1000); | 125 | // have later times for cache ejection priority, otherwise the test runs too fast to reliably |
| 126 | // differentiate | ||
| 127 | QThread::msleep(1000); | ||
| 128 | } | ||
| 127 | } | 129 | } |
| 130 | |||
| 128 | QCOMPARE(cache->getNumCachedFiles(), (size_t)0); | 131 | QCOMPARE(cache->getNumCachedFiles(), (size_t)0); |
| 129 | QCOMPARE(cache->getNumTotalFiles(), (size_t)10); | 132 | QCOMPARE(cache->getNumTotalFiles(), (size_t)10); |
| 130 | inUseFiles.clear(); | 133 | inUseFiles.clear(); |
| @@ -165,6 +168,20 @@ void FileCacheTests::testFreeSpacePreservation() { · 34 unmodified lines | |||
| 165 | } | 168 | } |
| 166 | } | 169 | } |
| 167 | 170 | ||
| 171 | void FileCacheTests::testWipe() { | ||
| 172 | // Reset the cache | ||
| 173 | auto cache = makeFileCache(_testDir.path()); | ||
| 174 | QCOMPARE(cache->getNumCachedFiles(), (size_t)5); | ||
| 175 | QCOMPARE(cache->getNumTotalFiles(), (size_t)5); | ||
| 176 | cache->wipe(); | ||
| 177 | QCOMPARE(cache->getNumCachedFiles(), (size_t)0); | ||
| 178 | QCOMPARE(cache->getNumTotalFiles(), (size_t)0); | ||
| 179 | QVERIFY(getCacheDirectorySize() > 0); | ||
| 180 | forceDeletes(); | ||
| 181 | QCOMPARE(getCacheDirectorySize(), (size_t)0); | ||
| 182 | } | ||
| 183 | |||
| 184 | |||
| 168 | void FileCacheTests::cleanupTestCase() { | 185 | void FileCacheTests::cleanupTestCase() { |
| 169 | } | 186 | } |
| 170 | 187 | ||
| @@ -20,6 +20,7 @@ private slots: · 19 unmodified lines | |||
| 20 | void testUnusedFiles(); | 20 | void testUnusedFiles(); |
| 21 | void testFreeSpacePreservation(); | 21 | void testFreeSpacePreservation(); |
| 22 | void cleanupTestCase(); | 22 | void cleanupTestCase(); |
| 23 | void testWipe(); | ||
| 23 | 24 | ||
| 24 | private: | 25 | private: |
| 25 | size_t getFreeSpace() const; | 26 | size_t getFreeSpace() const; |