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>
21template <
typename T>
class SharedAsset;
32template <
typename TAssetType,
typename TAssetKey>
33class CESIUMASYNC_API SharedAssetDepot
35 SharedAssetDepot<TAssetType, TAssetKey>>,
50 int64_t inactiveAssetSizeLimitBytes = 16 * 1024 * 1024;
69 const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
70 const TAssetKey& key);
95 const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
96 const TAssetKey& assetKey);
133 LockHolder lock()
const;
143 void markDeletionCandidate(
const TAssetType& asset,
bool threadOwnsDepotLock)
146 void markDeletionCandidateUnderLock(
const TAssetType& asset);
156 void unmarkDeletionCandidate(
157 const TAssetType& asset,
158 bool threadOwnsDepotLock)
override;
160 void unmarkDeletionCandidateUnderLock(
const TAssetType& asset);
168 AssetEntry(TAssetKey&& key_)
170 key(
std::move(key_)),
174 sizeInDeletionList(0),
175 deletionListPointers() {}
177 AssetEntry(
const TAssetKey& key_) : AssetEntry(TAssetKey(key_)) {}
188 std::unique_ptr<TAssetType> pAsset;
195 std::optional<SharedFuture<CesiumUtility::ResultPointer<TAssetType>>>
211 int64_t sizeInDeletionList;
236 std::unique_lock<std::mutex> lock;
241 std::unordered_map<TAssetKey, CesiumUtility::IntrusivePointer<AssetEntry>>
246 std::unordered_map<TAssetType*, AssetEntry*> _assetsByPointer;
255 int64_t _totalDeletionCandidateMemoryUsage;
259 mutable std::mutex _mutex;
262 std::function<FactorySignature> _factory;
271template <
typename TAssetType,
typename TAssetKey>
273 std::function<FactorySignature> factory)
276 _deletionCandidates(),
277 _totalDeletionCandidateMemoryUsage(0),
279 _factory(
std::move(factory)),
280 _pKeepAlive(nullptr) {}
282template <
typename TAssetType,
typename TAssetKey>
305 CESIUM_ASSERT(this->_assets.size() == this->_deletionCandidates.size());
308template <
typename TAssetType,
typename TAssetKey>
309SharedFuture<CesiumUtility::ResultPointer<TAssetType>>
312 const std::shared_ptr<IAssetAccessor>& pAssetAccessor,
313 const TAssetKey& assetKey) {
316 LockHolder lock = this->lock();
318 auto existingIt = this->_assets.find(assetKey);
319 if (existingIt != this->_assets.end()) {
322 const AssetEntry& entry = *existingIt->second;
323 if (entry.maybePendingAsset) {
325 return *entry.maybePendingAsset;
351 .thenImmediately([pDepot, pEntry, asyncSystem, pAssetAccessor]() {
352 return pDepot->_factory(asyncSystem, pAssetAccessor, pEntry->key);
354 .catchImmediately([](std::exception&& e) {
358 std::string(
"Error creating asset: ") + e.what()));
364 LockHolder lock = pDepot->lock();
367 result.pValue->_pDepot = pDepot.get();
368 pDepot->_assetsByPointer[result.pValue.get()] = pEntry.
get();
374 std::unique_ptr<TAssetType>(result.pValue.get());
375 pEntry->errorsAndWarnings = std::move(result.errors);
376 pEntry->maybePendingAsset.
reset();
381 pDepot->_pKeepAlive = pDepot;
383 return pEntry->toResultUnderLock();
387 std::move(future).share();
389 pEntry->maybePendingAsset = sharedFuture;
391 [[maybe_unused]]
bool added = this->_assets.emplace(assetKey, pEntry).second;
395 CESIUM_ASSERT(added);
404template <
typename TAssetType,
typename TAssetKey>
406 LockHolder lock = this->lock();
407 return this->_assets.size();
410template <
typename TAssetType,
typename TAssetKey>
412 LockHolder lock = this->lock();
413 return this->_assets.size() - this->_deletionCandidates.size();
416template <
typename TAssetType,
typename TAssetKey>
418 LockHolder lock = this->lock();
419 return this->_deletionCandidates.size();
422template <
typename TAssetType,
typename TAssetKey>
426 LockHolder lock = this->lock();
427 return this->_totalDeletionCandidateMemoryUsage;
430template <
typename TAssetType,
typename TAssetKey>
431typename SharedAssetDepot<TAssetType, TAssetKey>::LockHolder
433 return LockHolder{
this};
436template <
typename TAssetType,
typename TAssetKey>
437void SharedAssetDepot<TAssetType, TAssetKey>::markDeletionCandidate(
438 const TAssetType& asset,
439 bool threadOwnsDepotLock) {
440 if (threadOwnsDepotLock) {
441 this->markDeletionCandidateUnderLock(asset);
443 LockHolder lock = this->lock();
444 this->markDeletionCandidateUnderLock(asset);
448template <
typename TAssetType,
typename TAssetKey>
449void SharedAssetDepot<TAssetType, TAssetKey>::markDeletionCandidateUnderLock(
450 const TAssetType& asset) {
451 auto it = this->_assetsByPointer.find(
const_cast<TAssetType*
>(&asset));
452 CESIUM_ASSERT(it != this->_assetsByPointer.end());
453 if (it == this->_assetsByPointer.end()) {
457 CESIUM_ASSERT(it->second !=
nullptr);
459 AssetEntry& entry = *it->second;
460 entry.sizeInDeletionList = asset.getSizeBytes();
461 this->_totalDeletionCandidateMemoryUsage += entry.sizeInDeletionList;
463 this->_deletionCandidates.insertAtTail(entry);
465 if (this->_totalDeletionCandidateMemoryUsage >
466 this->inactiveAssetSizeLimitBytes) {
468 while (this->_deletionCandidates.size() > 0 &&
469 this->_totalDeletionCandidateMemoryUsage >
470 this->inactiveAssetSizeLimitBytes) {
471 AssetEntry* pOldEntry = this->_deletionCandidates.head();
472 this->_deletionCandidates.remove(*pOldEntry);
474 this->_totalDeletionCandidateMemoryUsage -= pOldEntry->sizeInDeletionList;
477 pOldEntry->pAsset ==
nullptr ||
478 pOldEntry->pAsset->_referenceCount == 0);
480 if (pOldEntry->pAsset) {
481 this->_assetsByPointer.erase(pOldEntry->pAsset.get());
485 this->_assets.erase(pOldEntry->key);
491 if (this->_assets.size() == this->_deletionCandidates.size()) {
492 this->_pKeepAlive.reset();
496template <
typename TAssetType,
typename TAssetKey>
497void SharedAssetDepot<TAssetType, TAssetKey>::unmarkDeletionCandidate(
498 const TAssetType& asset,
499 bool threadOwnsDepotLock) {
500 if (threadOwnsDepotLock) {
501 this->unmarkDeletionCandidateUnderLock(asset);
503 LockHolder lock = this->lock();
504 this->unmarkDeletionCandidateUnderLock(asset);
508template <
typename TAssetType,
typename TAssetKey>
509void SharedAssetDepot<TAssetType, TAssetKey>::unmarkDeletionCandidateUnderLock(
510 const TAssetType& asset) {
511 auto it = this->_assetsByPointer.find(
const_cast<TAssetType*
>(&asset));
512 CESIUM_ASSERT(it != this->_assetsByPointer.end());
513 if (it == this->_assetsByPointer.end()) {
517 CESIUM_ASSERT(it->second !=
nullptr);
519 AssetEntry& entry = *it->second;
520 bool isFound = this->_deletionCandidates.contains(entry);
522 CESIUM_ASSERT(isFound);
525 this->_totalDeletionCandidateMemoryUsage -= entry.sizeInDeletionList;
526 this->_deletionCandidates.remove(entry);
530 this->_pKeepAlive =
this;
533template <
typename TAssetType,
typename TAssetKey>
535SharedAssetDepot<TAssetType, TAssetKey>::AssetEntry::toResultUnderLock()
const {
542 pAsset->addReference(
true);
544 pAsset->releaseReference(
true);
549template <
typename TAssetType,
typename TAssetKey>
550SharedAssetDepot<TAssetType, TAssetKey>::LockHolder::LockHolder(
552 : pDepot(pDepot_), lock(pDepot_->_mutex) {}
554template <
typename TAssetType,
typename TAssetKey>
555SharedAssetDepot<TAssetType, TAssetKey>::LockHolder::~LockHolder() =
default;
557template <
typename TAssetType,
typename TAssetKey>
558void SharedAssetDepot<TAssetType, TAssetKey>::LockHolder::unlock() {
A system for managing asynchronous requests and tasks.
Future< T > createResolvedFuture(T &&value) const
Creates a future that is already resolved.
Promise< T > createPromise() const
Create a Promise that can be used at a later time to resolve or reject a Future.
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...
size_t getActiveAssetCount() const
Gets the number of assets owned by this depot that are active, meaning that they are currently being ...
size_t getInactiveAssetCount() const
Gets the number of assets owned by this depot that are inactive, meaning that they are not currently ...
size_t getAssetCount() const
Returns the total number of distinct assets contained in this depot, including both active and inacti...
CesiumAsync::Future< CesiumUtility::ResultPointer< TAssetType > >( const AsyncSystem &asyncSystem, const std::shared_ptr< IAssetAccessor > &pAssetAccessor, const TAssetKey &key) FactorySignature
Signature for the callback function that will be called to fetch and create a new instance of TAssetT...
SharedAssetDepot(std::function< FactorySignature > factory)
Creates a new SharedAssetDepot using the given factory callback to load new assets.
int64_t getInactiveAssetTotalSizeBytes() const
Gets the total bytes used by inactive (unused) assets owned by this depot.
SharedFuture< CesiumUtility::ResultPointer< TAssetType > > getOrCreate(const AsyncSystem &asyncSystem, const std::shared_ptr< IAssetAccessor > &pAssetAccessor, const TAssetKey &assetKey)
Gets an asset from the depot if it already exists, or creates it using the depot's factory if it does...
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.
A reference-counted base class, meant to be used with IntrusivePointer.
Classes that support asynchronous operations.
Utility classes for Cesium.
Result< IntrusivePointer< T > > ResultPointer
A convenient shortcut for CesiumUtility::Result<CesiumUtility::IntrusivePointer<T>>.
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....