From 9630e735dbc911c81e10a24befd5c563d5fd1980 Mon Sep 17 00:00:00 2001 From: Emil Axelsson Date: Thu, 7 Dec 2017 11:07:15 +0100 Subject: [PATCH] An asset can only be considered synchronized when all required assets are synchronized --- assets/solarsystem.asset | 3 +- include/openspace/scene/asset.h | 13 ++- include/openspace/scene/assetmanager.h | 5 +- src/scene/asset.cpp | 111 ++++++++++++++++++------- src/scene/assetmanager.cpp | 22 +++-- 5 files changed, 115 insertions(+), 39 deletions(-) diff --git a/assets/solarsystem.asset b/assets/solarsystem.asset index 746236f81a..de63601653 100644 --- a/assets/solarsystem.asset +++ b/assets/solarsystem.asset @@ -1,6 +1,7 @@ asset.require("keybindings") +asset.require("spice/base") +asset.require("sun/sun") -asset.request("sun/sun") --asset.request("planets/planets") -- asset.request("planets/moons") asset.request("planets/trails") diff --git a/include/openspace/scene/asset.h b/include/openspace/scene/asset.h index 7f84868a00..0f15d75f78 100644 --- a/include/openspace/scene/asset.h +++ b/include/openspace/scene/asset.h @@ -81,7 +81,11 @@ public: void setState(State state); void addSynchronization(std::shared_ptr synchronization); - std::vector> synchronizations() const; + std::vector> ownSynchronizations() const; + std::vector> requiredSynchronizations() const; + + void syncStateChanged(std::shared_ptr sync, + ResourceSynchronization::State s); // Sync bool isSynchronized(); @@ -103,10 +107,13 @@ public: void request(std::shared_ptr child); void unrequest(std::shared_ptr child); + std::vector> requestedAssets(); + std::vector> requestingAssets(); + std::vector> requiredAssets(); + std::vector> requiringAssets(); + std::vector> requiredSubTreeAssets(); std::vector> subTreeAssets(); - std::vector> requestedAssets(); - std::vector> requiredAssets(); std::vector> childAssets(); bool isRequired() const; diff --git a/include/openspace/scene/assetmanager.h b/include/openspace/scene/assetmanager.h index 31e97253be..70a93607be 100644 --- a/include/openspace/scene/assetmanager.h +++ b/include/openspace/scene/assetmanager.h @@ -69,9 +69,12 @@ public: private: std::shared_ptr tryAddAsset(const std::string& path); bool tryRemoveAsset(const std::string& path); - void assetStateChanged(Asset& asset, Asset::State state); + void assetStateChanged(std::shared_ptr asset, Asset::State state); std::unordered_map _pendingStateChangeCommands; + std::mutex _pendingInitializationsMutex; + std::vector> _pendingInitializations; + std::unique_ptr _assetLoader; AssetLoader::CallbackHandle _addAssetCallbackHandle; }; diff --git a/src/scene/asset.cpp b/src/scene/asset.cpp index cee22256b0..30657020bb 100644 --- a/src/scene/asset.cpp +++ b/src/scene/asset.cpp @@ -116,30 +116,57 @@ void Asset::addSynchronization(std::shared_ptr synchron // This will be called from another thread, so we need to mutex protect // things that are touched by the callback. ResourceSynchronization::CallbackHandle cbh = synchronization->addStateChangeCallback( - [this](ResourceSynchronization::State s) { - std::vector> syncs = - this->synchronizations(); - - if (s == ResourceSynchronization::State::Resolved) { - auto it = std::find_if( - syncs.begin(), - syncs.end(), - [](std::shared_ptr& s) { - return !s->isResolved(); - } - ); - if (it == syncs.end()) { - setState(State::SyncResolved); - } - } else if (s == ResourceSynchronization::State::Rejected) { - setState(State::SyncRejected); - } + [this, synchronization](ResourceSynchronization::State state) { + syncStateChanged(synchronization, state); } ); _syncCallbackHandles[synchronization.get()] = cbh; } + +void Asset::syncStateChanged(std::shared_ptr synchronization, + ResourceSynchronization::State state) +{ + if (state == ResourceSynchronization::State::Resolved) { + std::vector> requiredAssets = this->requiredAssets(); + auto pendingRequiredAsset = std::find_if( + requiredAssets.begin(), + requiredAssets.end(), + [](std::shared_ptr& a) { + return a->state() == Asset::State::Loaded || + a->state() == Asset::State::Synchronizing; + } + ); -std::vector> Asset::synchronizations() const { + if (pendingRequiredAsset != requiredAssets.end()) { + // Do not change state if required assets are still synchronizing + return; + } + + std::vector> syncs = this->ownSynchronizations(); + auto unresolvedOwnSynchronization = std::find_if( + syncs.begin(), + syncs.end(), + [](std::shared_ptr& s) { + return !s->isResolved(); + } + ); + if (unresolvedOwnSynchronization == syncs.end()) { + setState(State::SyncResolved); + } + } else if (state == ResourceSynchronization::State::Rejected) { + setState(State::SyncRejected); + } + + // Notify parents + std::vector> requiringAssets = this->requiringAssets(); + for (auto& a : requiringAssets) { + if (a->state() == Asset::State::Synchronizing) { + a->syncStateChanged(synchronization, state); + } + } +} + +std::vector> Asset::ownSynchronizations() const { std::lock_guard guard(_synchronizationsMutex); return _synchronizations; } @@ -177,19 +204,24 @@ bool Asset::startSynchronizations() { return false; } + setState(State::Synchronizing); + bool foundUnresolved = false; + // Start synchronization of all children first. - for (auto& child : childAssets()) { + for (auto& child : requiredAssets()) { if (child->startSynchronizations()) { foundUnresolved = true; } } + for (auto& child : requestedAssets()) { + child->startSynchronizations(); + } // Now synchronize its own synchronizations. - for (const auto& s : synchronizations()) { + for (const auto& s : ownSynchronizations()) { if (!s->isResolved()) { foundUnresolved = true; - setState(State::Synchronizing); s->start(); } } @@ -208,7 +240,7 @@ bool Asset::cancelSynchronizations() { cancelledAnySync = true; } } - for (const auto& s : synchronizations()) { + for (const auto& s : ownSynchronizations()) { if (s->isSyncing()) { cancelledAnySync = true; s->cancel(); @@ -234,7 +266,7 @@ float Asset::synchronizationProgress() { for (const auto& a : assets) { const std::vector> syncs = - a->synchronizations(); + a->ownSynchronizations(); for (const auto& sync : syncs) { if (sync->nTotalBytesIsKnown()) { @@ -254,7 +286,7 @@ float Asset::synchronizationProgress() { bool Asset::isInitReady() const { // An asset is ready for initialization if all synchronizations are resolved // and all its dependencies are ready for initialization. - for (const std::shared_ptr sync : synchronizations()) { + for (const std::shared_ptr& sync : ownSynchronizations()) { if (!sync->isResolved()) { return false; } @@ -384,9 +416,8 @@ bool Asset::requires(const Asset* asset) const { } void Asset::require(std::shared_ptr child) { - if (state() == Asset::State::Unloaded) { - // TODO: Throw: Can only require asset when in unloaded state - return; + if (state() != Asset::State::Unloaded) { + throw ghoul::RuntimeError("Cannot require child asset when already loaded"); } auto it = std::find(_requiredAssets.begin(), @@ -446,10 +477,34 @@ std::vector> Asset::requiredAssets() { return _requiredAssets; } +std::vector> Asset::requiringAssets() { + std::vector> assets; + assets.reserve(_requiringAssets.size()); + for (auto& a : _requiringAssets) { + std::shared_ptr shared = a.lock(); + if (shared) { + assets.push_back(shared); + } + } + return assets; +} + std::vector> Asset::requestedAssets() { return _requestedAssets; } +std::vector> Asset::requestingAssets() { + std::vector> assets; + assets.reserve(_requestingAssets.size()); + for (auto& a : _requestingAssets) { + std::shared_ptr shared = a.lock(); + if (shared) { + assets.push_back(shared); + } + } + return assets; +} + std::vector> Asset::childAssets() { std::vector> children; children.reserve(_requiredAssets.size() + _requestedAssets.size()); diff --git a/src/scene/assetmanager.cpp b/src/scene/assetmanager.cpp index 8c70249276..b954ca7178 100644 --- a/src/scene/assetmanager.cpp +++ b/src/scene/assetmanager.cpp @@ -45,13 +45,13 @@ void AssetManager::initialize() { _addAssetCallbackHandle = _assetLoader->addAssetLoadCallback( [this] (std::shared_ptr a) { a->addStateChangeCallback([a, this] (Asset::State state) { - assetStateChanged(*a, state); + assetStateChanged(a, state); }); } ); std::shared_ptr rootAsset = _assetLoader->rootAsset(); rootAsset->addStateChangeCallback([&rootAsset, this] (Asset::State state) { - assetStateChanged(*rootAsset, state); + assetStateChanged(rootAsset, state); }); rootAsset->initialize(); } @@ -62,6 +62,15 @@ void AssetManager::deinitialize() { } bool AssetManager::update() { + // Initialize assets + { + std::lock_guard guard(_pendingInitializationsMutex); + for (const auto& a : _pendingInitializations) { + a->initialize(); + } + _pendingInitializations.clear(); + } + // Add assets for (const auto& c : _pendingStateChangeCommands) { const std::string& path = c.first; @@ -84,16 +93,17 @@ bool AssetManager::update() { return false; } -void AssetManager::assetStateChanged(Asset& asset, Asset::State state) { +void AssetManager::assetStateChanged(std::shared_ptr asset, Asset::State state) { if (rootAsset()->state() == Asset::State::Initialized) { if (state == Asset::State::Loaded) { - asset.startSynchronizations(); + asset->startSynchronizations(); } if (state == Asset::State::SyncResolved) { - asset.initialize(); + std::lock_guard guard(_pendingInitializationsMutex); + _pendingInitializations.push_back(asset); } } else { - asset.deinitialize(); + asset->deinitialize(); } // Todo: Check if assets should start syncing or if they should init. // flags: autoSync, autoInit ?