3#include <CesiumAsync/AsyncSystem.h>
4#include <CesiumAsync/Future.h>
5#include <CesiumAsync/IAssetAccessor.h>
6#include <CesiumUtility/DoublyLinkedList.h>
7#include <CesiumUtility/IDepotOwningAsset.h>
8#include <CesiumUtility/IntrusivePointer.h>
9#include <CesiumUtility/ReferenceCounted.h>
10#include <CesiumUtility/Result.h>
18#include <unordered_map>
59 SharedAssetDepot<TAssetType, TAssetKey, TContext>>,
75 static_cast<int64_t
>(16 * 1024 * 1024);
93 const TContext& context,
94 const TAssetKey& key);
187 LockHolder lock()
const;
197 void markDeletionCandidate(
const TAssetType& asset,
bool threadOwnsDepotLock)
200 void markDeletionCandidateUnderLock(
const TAssetType& asset);
210 void unmarkDeletionCandidate(
211 const TAssetType& asset,
212 bool threadOwnsDepotLock)
override;
214 void unmarkDeletionCandidateUnderLock(
const TAssetType& asset);
222 bool invalidateUnderLock(LockHolder&& lock,
const TAssetKey& assetKey);
230 AssetEntry(TAssetKey&& key_)
232 key(
std::move(key_)),
236 sizeInDeletionList(0),
237 deletionListPointers() {}
239 AssetEntry(
const TAssetKey& key_) : AssetEntry(TAssetKey(key_)) {}
250 std::unique_ptr<TAssetType> pAsset;
257 std::optional<SharedFuture<CesiumUtility::ResultPointer<TAssetType>>>
273 int64_t sizeInDeletionList;
288 const CesiumUtility::IntrusivePointer<const SharedAssetDepot>& pDepot);
297 CesiumUtility::IntrusivePointer<const SharedAssetDepot> pDepot;
298 std::unique_lock<std::mutex> lock;
303 std::unordered_map<TAssetKey, CesiumUtility::IntrusivePointer<AssetEntry>>
308 std::unordered_map<TAssetType*, AssetEntry*> _assetsByPointer;
317 int64_t _totalDeletionCandidateMemoryUsage;
322 int64_t _liveInvalidatedAssets;
326 mutable std::mutex _mutex;
329 std::function<FactorySignature> _factory;
334 CesiumUtility::IntrusivePointer<
335 SharedAssetDepot<TAssetType, TAssetKey, TContext>>
339template <
typename TAssetType,
typename TAssetKey,
typename TContext>
341 std::function<FactorySignature> factory)
344 _deletionCandidates(),
345 _totalDeletionCandidateMemoryUsage(0),
346 _liveInvalidatedAssets(0),
348 _factory(
std::move(factory)),
349 _pKeepAlive(nullptr) {}
351template <
typename TAssetType,
typename TAssetKey,
typename TContext>
352SharedAssetDepot<TAssetType, TAssetKey, TContext>::~SharedAssetDepot() {
374 CESIUM_ASSERT(this->_liveInvalidatedAssets == 0);
375 CESIUM_ASSERT(this->_assets.size() == this->_deletionCandidates.size());
378template <
typename TAssetType,
typename TAssetKey,
typename TContext>
379SharedFuture<CesiumUtility::ResultPointer<TAssetType>>
381 const TContext& context,
382 const TAssetKey& assetKey) {
385 LockHolder lock = this->lock();
387 auto existingIt = this->_assets.find(assetKey);
388 if (existingIt != this->_assets.end()) {
391 const AssetEntry& entry = *existingIt->second;
392 if (entry.maybePendingAsset) {
394 return *entry.maybePendingAsset;
396 return context.asyncSystem.createResolvedFuture(entry.toResultUnderLock())
410 Promise<void> promise = context.asyncSystem.template createPromise<void>();
421 .thenImmediately([pDepot, pEntry, context]() {
422 return pDepot->_factory(context, pEntry->key);
428 LockHolder lock = pDepot->lock();
431 result.pValue->_pDepot = pDepot.get();
432 pDepot->_assetsByPointer[result.pValue.get()] = pEntry.
get();
438 std::unique_ptr<TAssetType>(result.pValue.get());
439 pEntry->errorsAndWarnings = std::move(result.errors);
440 pEntry->maybePendingAsset.
reset();
445 pDepot->_pKeepAlive = pDepot;
447 return pEntry->toResultUnderLock();
449 .catchImmediately([pDepot, pEntry](std::exception&& e) {
453 LockHolder lock = pDepot->lock();
454 pDepot->_assets.erase(pEntry->key);
460 std::string(
"Exception while creating asset: ") +
465 std::move(future).share();
467 pEntry->maybePendingAsset = sharedFuture;
469 [[maybe_unused]]
bool added = this->_assets.emplace(assetKey, pEntry).second;
473 CESIUM_ASSERT(added);
482template <
typename TAssetType,
typename TAssetKey,
typename TContext>
484 const TAssetKey& assetKey) {
485 LockHolder lock = this->lock();
486 return this->invalidateUnderLock(std::move(lock), assetKey);
489template <
typename TAssetType,
typename TAssetKey,
typename TContext>
492 LockHolder lock = this->lock();
494 auto it = this->_assetsByPointer.find(&asset);
495 if (it == this->_assetsByPointer.end())
498 AssetEntry* pEntry = it->second;
499 CESIUM_ASSERT(pEntry);
501 return this->invalidateUnderLock(std::move(lock), pEntry->key);
504template <
typename TAssetType,
typename TAssetKey,
typename TContext>
507 LockHolder lock = this->lock();
508 return this->_assets.size();
511template <
typename TAssetType,
typename TAssetKey,
typename TContext>
514 LockHolder lock = this->lock();
515 return this->_assets.size() - this->_deletionCandidates.size();
518template <
typename TAssetType,
typename TAssetKey,
typename TContext>
522 LockHolder lock = this->lock();
523 return this->_deletionCandidates.size();
526template <
typename TAssetType,
typename TAssetKey,
typename TContext>
529 LockHolder lock = this->lock();
530 return this->_totalDeletionCandidateMemoryUsage;
533template <
typename TAssetType,
typename TAssetKey,
typename TContext>
534typename SharedAssetDepot<TAssetType, TAssetKey, TContext>::LockHolder
535SharedAssetDepot<TAssetType, TAssetKey, TContext>::lock()
const {
536 return LockHolder{
this};
539template <
typename TAssetType,
typename TAssetKey,
typename TContext>
540void SharedAssetDepot<TAssetType, TAssetKey, TContext>::markDeletionCandidate(
541 const TAssetType& asset,
542 bool threadOwnsDepotLock) {
543 if (threadOwnsDepotLock) {
544 this->markDeletionCandidateUnderLock(asset);
546 LockHolder lock = this->lock();
547 this->markDeletionCandidateUnderLock(asset);
551template <
typename TAssetType,
typename TAssetKey,
typename TContext>
554 if (asset._isInvalidated) {
556 --this->_liveInvalidatedAssets;
561 if (this->_assets.size() == this->_deletionCandidates.size() &&
562 this->_liveInvalidatedAssets == 0) {
563 this->_pKeepAlive.reset();
571 if (asset._referenceCount != 0) {
575 auto it = this->_assetsByPointer.find(
const_cast<TAssetType*
>(&asset));
576 CESIUM_ASSERT(it != this->_assetsByPointer.end());
577 if (it == this->_assetsByPointer.end()) {
581 CESIUM_ASSERT(it->second !=
nullptr);
583 AssetEntry& entry = *it->second;
584 entry.sizeInDeletionList = asset.getSizeBytes();
585 this->_totalDeletionCandidateMemoryUsage += entry.sizeInDeletionList;
587 this->_deletionCandidates.insertAtTail(entry);
589 if (this->_totalDeletionCandidateMemoryUsage >
590 this->inactiveAssetSizeLimitBytes) {
592 while (this->_deletionCandidates.size() > 0 &&
593 this->_totalDeletionCandidateMemoryUsage >
594 this->inactiveAssetSizeLimitBytes) {
595 AssetEntry* pOldEntry = this->_deletionCandidates.head();
596 this->_deletionCandidates.remove(*pOldEntry);
598 this->_totalDeletionCandidateMemoryUsage -= pOldEntry->sizeInDeletionList;
601 pOldEntry->pAsset ==
nullptr ||
602 pOldEntry->pAsset->_referenceCount == 0);
604 if (pOldEntry->pAsset) {
605 this->_assetsByPointer.erase(pOldEntry->pAsset.get());
609 this->_assets.erase(pOldEntry->key);
615 if (this->_assets.size() == this->_deletionCandidates.size() &&
616 this->_liveInvalidatedAssets == 0) {
617 this->_pKeepAlive.reset();
621template <
typename TAssetType,
typename TAssetKey,
typename TContext>
622void SharedAssetDepot<TAssetType, TAssetKey, TContext>::unmarkDeletionCandidate(
623 const TAssetType& asset,
624 bool threadOwnsDepotLock) {
625 if (threadOwnsDepotLock) {
626 this->unmarkDeletionCandidateUnderLock(asset);
628 LockHolder lock = this->lock();
629 this->unmarkDeletionCandidateUnderLock(asset);
633template <
typename TAssetType,
typename TAssetKey,
typename TContext>
640 CESIUM_ASSERT(!asset._isInvalidated);
642 auto it = this->_assetsByPointer.find(
const_cast<TAssetType*
>(&asset));
643 CESIUM_ASSERT(it != this->_assetsByPointer.end());
644 if (it == this->_assetsByPointer.end()) {
648 CESIUM_ASSERT(it->second !=
nullptr);
650 AssetEntry& entry = *it->second;
651 bool isFound = this->_deletionCandidates.contains(entry);
656 this->_totalDeletionCandidateMemoryUsage -= entry.sizeInDeletionList;
657 this->_deletionCandidates.remove(entry);
661 this->_pKeepAlive =
this;
664template <
typename TAssetType,
typename TAssetKey,
typename TContext>
665bool SharedAssetDepot<TAssetType, TAssetKey, TContext>::invalidateUnderLock(
667 const TAssetKey& assetKey) {
668 auto it = this->_assets.find(assetKey);
669 if (it == this->_assets.end())
672 AssetEntry* pEntry = it->second.get();
673 CESIUM_ASSERT(pEntry);
678 pEntry->toResultUnderLock();
680 bool wasInvalidated =
false;
682 if (assetResult.pValue) {
683 if (!assetResult.pValue->_isInvalidated) {
684 wasInvalidated =
true;
685 assetResult.pValue->_isInvalidated =
true;
686 ++this->_liveInvalidatedAssets;
688 this->_assetsByPointer.erase(assetResult.pValue.get());
693 pEntry->pAsset.release();
698 this->_assets.erase(it);
705 return wasInvalidated;
708template <
typename TAssetType,
typename TAssetKey,
typename TContext>
716 CesiumUtility::IntrusivePointer<TAssetType> p =
nullptr;
718 pAsset->addReference(
true);
720 pAsset->releaseReference(
true);
725template <
typename TAssetType,
typename TAssetKey,
typename TContext>
727 const CesiumUtility::IntrusivePointer<const SharedAssetDepot>& pDepot_)
728 : pDepot(pDepot_), lock(pDepot_->_mutex) {}
730template <
typename TAssetType,
typename TAssetKey,
typename TContext>
731SharedAssetDepot<TAssetType, TAssetKey, TContext>::LockHolder::~LockHolder() =
734template <
typename TAssetType,
typename TAssetKey,
typename TContext>
735void SharedAssetDepot<TAssetType, TAssetKey, TContext>::LockHolder::unlock() {
A system for managing asynchronous requests and tasks.
A value that will be available in the future, as produced by AsyncSystem.
A promise that can be resolved or rejected by an asynchronous task.
void resolve(T &&value) const
Will be called when the task completed successfully.
Future< T > getFuture() const
Gets the Future that resolves or rejects when this Promise is resolved or rejected.
A depot for CesiumUtility::SharedAsset instances, which are potentially shared between multiple objec...
SharedAssetDepot(std::function< FactorySignature > factory)
Creates a new SharedAssetDepot using the given factory callback to load new assets.
CesiumAsync::Future< CesiumUtility::ResultPointer< TAssetType > >( const TContext &context, const TAssetKey &key) FactorySignature
Signature for the callback function that will be called to fetch and create a new instance of TAssetT...
std::atomic< int64_t > inactiveAssetSizeLimitBytes
int64_t getInactiveAssetTotalSizeBytes() const
Gets the total bytes used by inactive (unused) assets owned by this depot.
size_t getActiveAssetCount() const
Gets the number of assets owned by this depot that are active, meaning that they are currently being ...
bool invalidate(TAssetType &asset)
Invalidates the previously-cached asset, so that the next call to getOrCreate will create the asset i...
SharedFuture< CesiumUtility::ResultPointer< TAssetType > > getOrCreate(const TContext &context, const TAssetKey &assetKey)
Gets an asset from the depot if it already exists, or creates it using the depot's factory if it does...
size_t getInactiveAssetCount() const
Gets the number of assets owned by this depot that are inactive, meaning that they are not currently ...
bool invalidate(const TAssetKey &assetKey)
Invalidates the previously-cached asset with the given key, so that the next call to getOrCreate will...
size_t getAssetCount() const
Returns the total number of distinct assets contained in this depot, including both active and inacti...
A value that will be available in the future, as produced by AsyncSystem. Unlike Future,...
Contains the previous and next pointers for an element in a DoublyLinkedList.
An interface representing the depot that owns a SharedAsset. This interface is an implementation deta...
A smart pointer that calls addReference and releaseReference on the controlled object.
void reset()
Reset this pointer to nullptr.
T * get() const noexcept
Returns the internal pointer.
An asset that is potentially shared between multiple objects, such as an image shared between multipl...
Classes that support asynchronous operations.
Utility classes for Cesium.
Result< IntrusivePointer< T > > ResultPointer
A convenient shortcut for CesiumUtility::Result<CesiumUtility::IntrusivePointer<T>>.
DoublyLinkedListAdvanced< T, T, Pointers > DoublyLinkedList
An intrusive doubly-linked list.
ReferenceCounted< T, true > ReferenceCountedThreadSafe
A reference-counted base class, meant to be used with IntrusivePointer. The reference count is thread...
The default context passed to SharedAssetDepot factory functions.
std::shared_ptr< IAssetAccessor > pAssetAccessor
The asset accessor.
AsyncSystem asyncSystem
The async system.
The container to store the error and warning list when loading a tile or glTF content.
static ErrorList error(std::string errorMessage)
Creates an ErrorList containing a single error.
Holds the result of an operation. If the operation succeeds, it will provide a value....