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>
58 SharedAssetDepot<TAssetType, TAssetKey, TContext>>,
74 static_cast<int64_t
>(16 * 1024 * 1024);
92 const TContext& context,
93 const TAssetKey& key);
186 LockHolder lock()
const;
196 void markDeletionCandidate(
const TAssetType& asset,
bool threadOwnsDepotLock)
199 void markDeletionCandidateUnderLock(
const TAssetType& asset);
209 void unmarkDeletionCandidate(
210 const TAssetType& asset,
211 bool threadOwnsDepotLock)
override;
213 void unmarkDeletionCandidateUnderLock(
const TAssetType& asset);
221 bool invalidateUnderLock(LockHolder&& lock,
const TAssetKey& assetKey);
229 AssetEntry(TAssetKey&& key_)
231 key(
std::move(key_)),
235 sizeInDeletionList(0),
236 deletionListPointers() {}
238 AssetEntry(
const TAssetKey& key_) : AssetEntry(TAssetKey(key_)) {}
249 std::unique_ptr<TAssetType> pAsset;
256 std::optional<SharedFuture<CesiumUtility::ResultPointer<TAssetType>>>
272 int64_t sizeInDeletionList;
287 const CesiumUtility::IntrusivePointer<const SharedAssetDepot>& pDepot);
296 CesiumUtility::IntrusivePointer<const SharedAssetDepot> pDepot;
297 std::unique_lock<std::mutex> lock;
302 std::unordered_map<TAssetKey, CesiumUtility::IntrusivePointer<AssetEntry>>
307 std::unordered_map<TAssetType*, AssetEntry*> _assetsByPointer;
316 int64_t _totalDeletionCandidateMemoryUsage;
321 int64_t _liveInvalidatedAssets;
325 mutable std::mutex _mutex;
328 std::function<FactorySignature> _factory;
333 CesiumUtility::IntrusivePointer<
334 SharedAssetDepot<TAssetType, TAssetKey, TContext>>
338template <
typename TAssetType,
typename TAssetKey,
typename TContext>
340 std::function<FactorySignature> factory)
343 _deletionCandidates(),
344 _totalDeletionCandidateMemoryUsage(0),
345 _liveInvalidatedAssets(0),
347 _factory(
std::move(factory)),
348 _pKeepAlive(nullptr) {}
350template <
typename TAssetType,
typename TAssetKey,
typename TContext>
351SharedAssetDepot<TAssetType, TAssetKey, TContext>::~SharedAssetDepot() {
373 CESIUM_ASSERT(this->_liveInvalidatedAssets == 0);
374 CESIUM_ASSERT(this->_assets.size() == this->_deletionCandidates.size());
377template <
typename TAssetType,
typename TAssetKey,
typename TContext>
378SharedFuture<CesiumUtility::ResultPointer<TAssetType>>
380 const TContext& context,
381 const TAssetKey& assetKey) {
384 LockHolder lock = this->lock();
386 auto existingIt = this->_assets.find(assetKey);
387 if (existingIt != this->_assets.end()) {
390 const AssetEntry& entry = *existingIt->second;
391 if (entry.maybePendingAsset) {
393 return *entry.maybePendingAsset;
395 return context.asyncSystem.createResolvedFuture(entry.toResultUnderLock())
409 Promise<void> promise = context.asyncSystem.template createPromise<void>();
420 .thenImmediately([pDepot, pEntry, context]() {
421 return pDepot->_factory(context, pEntry->key);
423 .catchImmediately([](std::exception&& e) {
427 std::string(
"Error creating asset: ") + e.what()));
433 LockHolder lock = pDepot->lock();
436 result.pValue->_pDepot = pDepot.get();
437 pDepot->_assetsByPointer[result.pValue.get()] = pEntry.
get();
443 std::unique_ptr<TAssetType>(result.pValue.get());
444 pEntry->errorsAndWarnings = std::move(result.errors);
445 pEntry->maybePendingAsset.
reset();
450 pDepot->_pKeepAlive = pDepot;
452 return pEntry->toResultUnderLock();
456 std::move(future).share();
458 pEntry->maybePendingAsset = sharedFuture;
460 [[maybe_unused]]
bool added = this->_assets.emplace(assetKey, pEntry).second;
464 CESIUM_ASSERT(added);
473template <
typename TAssetType,
typename TAssetKey,
typename TContext>
475 const TAssetKey& assetKey) {
476 LockHolder lock = this->lock();
477 return this->invalidateUnderLock(std::move(lock), assetKey);
480template <
typename TAssetType,
typename TAssetKey,
typename TContext>
483 LockHolder lock = this->lock();
485 auto it = this->_assetsByPointer.find(&asset);
486 if (it == this->_assetsByPointer.end())
489 AssetEntry* pEntry = it->second;
490 CESIUM_ASSERT(pEntry);
492 return this->invalidateUnderLock(std::move(lock), pEntry->key);
495template <
typename TAssetType,
typename TAssetKey,
typename TContext>
498 LockHolder lock = this->lock();
499 return this->_assets.size();
502template <
typename TAssetType,
typename TAssetKey,
typename TContext>
505 LockHolder lock = this->lock();
506 return this->_assets.size() - this->_deletionCandidates.size();
509template <
typename TAssetType,
typename TAssetKey,
typename TContext>
513 LockHolder lock = this->lock();
514 return this->_deletionCandidates.size();
517template <
typename TAssetType,
typename TAssetKey,
typename TContext>
520 LockHolder lock = this->lock();
521 return this->_totalDeletionCandidateMemoryUsage;
524template <
typename TAssetType,
typename TAssetKey,
typename TContext>
525typename SharedAssetDepot<TAssetType, TAssetKey, TContext>::LockHolder
526SharedAssetDepot<TAssetType, TAssetKey, TContext>::lock()
const {
527 return LockHolder{
this};
530template <
typename TAssetType,
typename TAssetKey,
typename TContext>
531void SharedAssetDepot<TAssetType, TAssetKey, TContext>::markDeletionCandidate(
532 const TAssetType& asset,
533 bool threadOwnsDepotLock) {
534 if (threadOwnsDepotLock) {
535 this->markDeletionCandidateUnderLock(asset);
537 LockHolder lock = this->lock();
538 this->markDeletionCandidateUnderLock(asset);
542template <
typename TAssetType,
typename TAssetKey,
typename TContext>
545 if (asset._isInvalidated) {
547 --this->_liveInvalidatedAssets;
552 if (this->_assets.size() == this->_deletionCandidates.size() &&
553 this->_liveInvalidatedAssets == 0) {
554 this->_pKeepAlive.reset();
562 if (asset._referenceCount != 0) {
566 auto it = this->_assetsByPointer.find(
const_cast<TAssetType*
>(&asset));
567 CESIUM_ASSERT(it != this->_assetsByPointer.end());
568 if (it == this->_assetsByPointer.end()) {
572 CESIUM_ASSERT(it->second !=
nullptr);
574 AssetEntry& entry = *it->second;
575 entry.sizeInDeletionList = asset.getSizeBytes();
576 this->_totalDeletionCandidateMemoryUsage += entry.sizeInDeletionList;
578 this->_deletionCandidates.insertAtTail(entry);
580 if (this->_totalDeletionCandidateMemoryUsage >
581 this->inactiveAssetSizeLimitBytes) {
583 while (this->_deletionCandidates.size() > 0 &&
584 this->_totalDeletionCandidateMemoryUsage >
585 this->inactiveAssetSizeLimitBytes) {
586 AssetEntry* pOldEntry = this->_deletionCandidates.head();
587 this->_deletionCandidates.remove(*pOldEntry);
589 this->_totalDeletionCandidateMemoryUsage -= pOldEntry->sizeInDeletionList;
592 pOldEntry->pAsset ==
nullptr ||
593 pOldEntry->pAsset->_referenceCount == 0);
595 if (pOldEntry->pAsset) {
596 this->_assetsByPointer.erase(pOldEntry->pAsset.get());
600 this->_assets.erase(pOldEntry->key);
606 if (this->_assets.size() == this->_deletionCandidates.size() &&
607 this->_liveInvalidatedAssets == 0) {
608 this->_pKeepAlive.reset();
612template <
typename TAssetType,
typename TAssetKey,
typename TContext>
613void SharedAssetDepot<TAssetType, TAssetKey, TContext>::unmarkDeletionCandidate(
614 const TAssetType& asset,
615 bool threadOwnsDepotLock) {
616 if (threadOwnsDepotLock) {
617 this->unmarkDeletionCandidateUnderLock(asset);
619 LockHolder lock = this->lock();
620 this->unmarkDeletionCandidateUnderLock(asset);
624template <
typename TAssetType,
typename TAssetKey,
typename TContext>
631 CESIUM_ASSERT(!asset._isInvalidated);
633 auto it = this->_assetsByPointer.find(
const_cast<TAssetType*
>(&asset));
634 CESIUM_ASSERT(it != this->_assetsByPointer.end());
635 if (it == this->_assetsByPointer.end()) {
639 CESIUM_ASSERT(it->second !=
nullptr);
641 AssetEntry& entry = *it->second;
642 bool isFound = this->_deletionCandidates.contains(entry);
647 this->_totalDeletionCandidateMemoryUsage -= entry.sizeInDeletionList;
648 this->_deletionCandidates.remove(entry);
652 this->_pKeepAlive =
this;
655template <
typename TAssetType,
typename TAssetKey,
typename TContext>
656bool SharedAssetDepot<TAssetType, TAssetKey, TContext>::invalidateUnderLock(
658 const TAssetKey& assetKey) {
659 auto it = this->_assets.find(assetKey);
660 if (it == this->_assets.end())
663 AssetEntry* pEntry = it->second.get();
664 CESIUM_ASSERT(pEntry);
669 pEntry->toResultUnderLock();
671 bool wasInvalidated =
false;
673 if (assetResult.pValue) {
674 if (!assetResult.pValue->_isInvalidated) {
675 wasInvalidated =
true;
676 assetResult.pValue->_isInvalidated =
true;
677 ++this->_liveInvalidatedAssets;
679 this->_assetsByPointer.erase(assetResult.pValue.get());
684 pEntry->pAsset.release();
689 this->_assets.erase(it);
696 return wasInvalidated;
699template <
typename TAssetType,
typename TAssetKey,
typename TContext>
707 CesiumUtility::IntrusivePointer<TAssetType> p =
nullptr;
709 pAsset->addReference(
true);
711 pAsset->releaseReference(
true);
716template <
typename TAssetType,
typename TAssetKey,
typename TContext>
718 const CesiumUtility::IntrusivePointer<const SharedAssetDepot>& pDepot_)
719 : pDepot(pDepot_), lock(pDepot_->_mutex) {}
721template <
typename TAssetType,
typename TAssetKey,
typename TContext>
722SharedAssetDepot<TAssetType, TAssetKey, TContext>::LockHolder::~LockHolder() =
725template <
typename TAssetType,
typename TAssetKey,
typename TContext>
726void 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....