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);
427 LockHolder lock = pDepot->lock();
430 result.pValue->_pDepot = pDepot.get();
431 pDepot->_assetsByPointer[result.pValue.get()] = pEntry.
get();
437 std::unique_ptr<TAssetType>(result.pValue.get());
438 pEntry->errorsAndWarnings = std::move(result.errors);
439 pEntry->maybePendingAsset.
reset();
444 pDepot->_pKeepAlive = pDepot;
446 return pEntry->toResultUnderLock();
448 .catchImmediately([pDepot, pEntry](std::exception&& e) {
452 LockHolder lock = pDepot->lock();
453 pDepot->_assets.erase(pEntry->key);
459 std::string(
"Exception while creating asset: ") +
464 std::move(future).share();
466 pEntry->maybePendingAsset = sharedFuture;
468 [[maybe_unused]]
bool added = this->_assets.emplace(assetKey, pEntry).second;
472 CESIUM_ASSERT(added);
481template <
typename TAssetType,
typename TAssetKey,
typename TContext>
483 const TAssetKey& assetKey) {
484 LockHolder lock = this->lock();
485 return this->invalidateUnderLock(std::move(lock), assetKey);
488template <
typename TAssetType,
typename TAssetKey,
typename TContext>
491 LockHolder lock = this->lock();
493 auto it = this->_assetsByPointer.find(&asset);
494 if (it == this->_assetsByPointer.end())
497 AssetEntry* pEntry = it->second;
498 CESIUM_ASSERT(pEntry);
500 return this->invalidateUnderLock(std::move(lock), pEntry->key);
503template <
typename TAssetType,
typename TAssetKey,
typename TContext>
506 LockHolder lock = this->lock();
507 return this->_assets.size();
510template <
typename TAssetType,
typename TAssetKey,
typename TContext>
513 LockHolder lock = this->lock();
514 return this->_assets.size() - this->_deletionCandidates.size();
517template <
typename TAssetType,
typename TAssetKey,
typename TContext>
521 LockHolder lock = this->lock();
522 return this->_deletionCandidates.size();
525template <
typename TAssetType,
typename TAssetKey,
typename TContext>
528 LockHolder lock = this->lock();
529 return this->_totalDeletionCandidateMemoryUsage;
532template <
typename TAssetType,
typename TAssetKey,
typename TContext>
533typename SharedAssetDepot<TAssetType, TAssetKey, TContext>::LockHolder
534SharedAssetDepot<TAssetType, TAssetKey, TContext>::lock()
const {
535 return LockHolder{
this};
538template <
typename TAssetType,
typename TAssetKey,
typename TContext>
539void SharedAssetDepot<TAssetType, TAssetKey, TContext>::markDeletionCandidate(
540 const TAssetType& asset,
541 bool threadOwnsDepotLock) {
542 if (threadOwnsDepotLock) {
543 this->markDeletionCandidateUnderLock(asset);
545 LockHolder lock = this->lock();
546 this->markDeletionCandidateUnderLock(asset);
550template <
typename TAssetType,
typename TAssetKey,
typename TContext>
553 if (asset._isInvalidated) {
555 --this->_liveInvalidatedAssets;
560 if (this->_assets.size() == this->_deletionCandidates.size() &&
561 this->_liveInvalidatedAssets == 0) {
562 this->_pKeepAlive.reset();
570 if (asset._referenceCount != 0) {
574 auto it = this->_assetsByPointer.find(
const_cast<TAssetType*
>(&asset));
575 CESIUM_ASSERT(it != this->_assetsByPointer.end());
576 if (it == this->_assetsByPointer.end()) {
580 CESIUM_ASSERT(it->second !=
nullptr);
582 AssetEntry& entry = *it->second;
583 entry.sizeInDeletionList = asset.getSizeBytes();
584 this->_totalDeletionCandidateMemoryUsage += entry.sizeInDeletionList;
586 this->_deletionCandidates.insertAtTail(entry);
588 if (this->_totalDeletionCandidateMemoryUsage >
589 this->inactiveAssetSizeLimitBytes) {
591 while (this->_deletionCandidates.size() > 0 &&
592 this->_totalDeletionCandidateMemoryUsage >
593 this->inactiveAssetSizeLimitBytes) {
594 AssetEntry* pOldEntry = this->_deletionCandidates.head();
595 this->_deletionCandidates.remove(*pOldEntry);
597 this->_totalDeletionCandidateMemoryUsage -= pOldEntry->sizeInDeletionList;
600 pOldEntry->pAsset ==
nullptr ||
601 pOldEntry->pAsset->_referenceCount == 0);
603 if (pOldEntry->pAsset) {
604 this->_assetsByPointer.erase(pOldEntry->pAsset.get());
608 this->_assets.erase(pOldEntry->key);
614 if (this->_assets.size() == this->_deletionCandidates.size() &&
615 this->_liveInvalidatedAssets == 0) {
616 this->_pKeepAlive.reset();
620template <
typename TAssetType,
typename TAssetKey,
typename TContext>
621void SharedAssetDepot<TAssetType, TAssetKey, TContext>::unmarkDeletionCandidate(
622 const TAssetType& asset,
623 bool threadOwnsDepotLock) {
624 if (threadOwnsDepotLock) {
625 this->unmarkDeletionCandidateUnderLock(asset);
627 LockHolder lock = this->lock();
628 this->unmarkDeletionCandidateUnderLock(asset);
632template <
typename TAssetType,
typename TAssetKey,
typename TContext>
639 CESIUM_ASSERT(!asset._isInvalidated);
641 auto it = this->_assetsByPointer.find(
const_cast<TAssetType*
>(&asset));
642 CESIUM_ASSERT(it != this->_assetsByPointer.end());
643 if (it == this->_assetsByPointer.end()) {
647 CESIUM_ASSERT(it->second !=
nullptr);
649 AssetEntry& entry = *it->second;
650 bool isFound = this->_deletionCandidates.contains(entry);
655 this->_totalDeletionCandidateMemoryUsage -= entry.sizeInDeletionList;
656 this->_deletionCandidates.remove(entry);
660 this->_pKeepAlive =
this;
663template <
typename TAssetType,
typename TAssetKey,
typename TContext>
664bool SharedAssetDepot<TAssetType, TAssetKey, TContext>::invalidateUnderLock(
666 const TAssetKey& assetKey) {
667 auto it = this->_assets.find(assetKey);
668 if (it == this->_assets.end())
671 AssetEntry* pEntry = it->second.get();
672 CESIUM_ASSERT(pEntry);
677 pEntry->toResultUnderLock();
679 bool wasInvalidated =
false;
681 if (assetResult.pValue) {
682 if (!assetResult.pValue->_isInvalidated) {
683 wasInvalidated =
true;
684 assetResult.pValue->_isInvalidated =
true;
685 ++this->_liveInvalidatedAssets;
687 this->_assetsByPointer.erase(assetResult.pValue.get());
692 pEntry->pAsset.release();
697 this->_assets.erase(it);
704 return wasInvalidated;
707template <
typename TAssetType,
typename TAssetKey,
typename TContext>
715 CesiumUtility::IntrusivePointer<TAssetType> p =
nullptr;
717 pAsset->addReference(
true);
719 pAsset->releaseReference(
true);
724template <
typename TAssetType,
typename TAssetKey,
typename TContext>
726 const CesiumUtility::IntrusivePointer<const SharedAssetDepot>& pDepot_)
727 : pDepot(pDepot_), lock(pDepot_->_mutex) {}
729template <
typename TAssetType,
typename TAssetKey,
typename TContext>
730SharedAssetDepot<TAssetType, TAssetKey, TContext>::LockHolder::~LockHolder() =
733template <
typename TAssetType,
typename TAssetKey,
typename TContext>
734void 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....