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