155 lines
5.2 KiB
C
155 lines
5.2 KiB
C
|
|
#pragma once
|
||
|
|
|
||
|
|
#include <CesiumUtility/Assert.h>
|
||
|
|
#include <CesiumUtility/ExtensibleObject.h>
|
||
|
|
#include <CesiumUtility/IDepotOwningAsset.h>
|
||
|
|
#include <CesiumUtility/Library.h>
|
||
|
|
|
||
|
|
#include <atomic>
|
||
|
|
|
||
|
|
namespace CesiumAsync {
|
||
|
|
template <typename TAssetType, typename TAssetKey> class SharedAssetDepot;
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace CesiumUtility {
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief An asset that is potentially shared between multiple objects, such as
|
||
|
|
* an image shared between multiple glTF models. This is intended to be the base
|
||
|
|
* class for such assets.
|
||
|
|
*
|
||
|
|
* The lifetime of instances of this class should be managed by reference
|
||
|
|
* counting with {@link IntrusivePointer}.
|
||
|
|
*
|
||
|
|
* @tparam T The type that is _deriving_ from this class. For example, you
|
||
|
|
* should declare your class as
|
||
|
|
* `class MyClass : public SharedAsset<MyClass> { ... };`
|
||
|
|
*
|
||
|
|
* @remarks @parblock
|
||
|
|
* A `SharedAsset` can be in one of three possible states:
|
||
|
|
*
|
||
|
|
* **Independent Asset**
|
||
|
|
* An independent asset isn't affiliated with an asset depot at all.
|
||
|
|
* Its lifetime is controlled exclusively by IntrusivePointer / reference
|
||
|
|
* counting. When the asset's reference count goes to zero, it deletes itself.
|
||
|
|
* An independent asset's {@link getDepot} returns nullptr.
|
||
|
|
*
|
||
|
|
* **Active Depot Asset**
|
||
|
|
* This is an asset that is owned by an asset depot and that is in use, meaning
|
||
|
|
* it has a reference count greater than zero. The asset depot owns the asset
|
||
|
|
* via an `std::unique_ptr`, not via adding to the reference count. So when the
|
||
|
|
* reference count goes to zero, only the asset depot itself still has a
|
||
|
|
* reference to it, so it becomes an inactive depot asset.
|
||
|
|
*
|
||
|
|
* **Inactive Depot Asset**
|
||
|
|
* This is also an asset that is owned by the asset depot, but there are no
|
||
|
|
* other references to it (it has a reference count of zero). It is found in the
|
||
|
|
* asset depot's `deletionCandidates` list. When a reference to it is added, it
|
||
|
|
* is removed from `deletionCandidates` and it becomes an active depot asset.
|
||
|
|
* @endparblock
|
||
|
|
*/
|
||
|
|
template <typename T>
|
||
|
|
class CESIUMUTILITY_API SharedAsset : public CesiumUtility::ExtensibleObject {
|
||
|
|
public:
|
||
|
|
/**
|
||
|
|
* @brief Adds a counted reference to this object. Use
|
||
|
|
* {@link CesiumUtility::IntrusivePointer} instead of calling this method
|
||
|
|
* directly.
|
||
|
|
*/
|
||
|
|
void addReference() const noexcept { this->addReference(false); }
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Removes a counted reference from this object. When the last
|
||
|
|
* reference is removed, this method will delete this instance. Use
|
||
|
|
* {@link CesiumUtility::IntrusivePointer} instead of calling this method
|
||
|
|
* directly.
|
||
|
|
*/
|
||
|
|
void releaseReference() const noexcept { this->releaseReference(false); }
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Gets the shared asset depot that owns this asset, or nullptr if this
|
||
|
|
* asset is independent of an asset depot.
|
||
|
|
*/
|
||
|
|
const IDepotOwningAsset<T>* getDepot() const { return this->_pDepot; }
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @brief Gets the shared asset depot that owns this asset, or nullptr if this
|
||
|
|
* asset is independent of an asset depot.
|
||
|
|
*/
|
||
|
|
IDepotOwningAsset<T>* getDepot() { return this->_pDepot; }
|
||
|
|
|
||
|
|
protected:
|
||
|
|
SharedAsset() = default;
|
||
|
|
~SharedAsset() { CESIUM_ASSERT(this->_referenceCount == 0); }
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Assets can be copied, but the fresh instance has no references and is not
|
||
|
|
* in the asset depot.
|
||
|
|
*/
|
||
|
|
SharedAsset(const SharedAsset& rhs)
|
||
|
|
: ExtensibleObject(rhs), _referenceCount(0), _pDepot(nullptr) {}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* After a move construction, the content of the asset is moved to the new
|
||
|
|
* instance, but the asset depot still references the old instance.
|
||
|
|
*/
|
||
|
|
SharedAsset(SharedAsset&& rhs)
|
||
|
|
: ExtensibleObject(std::move(rhs)),
|
||
|
|
_referenceCount(0),
|
||
|
|
_pDepot(nullptr) {}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Assignment does not affect the asset's relationship with the depot, but is
|
||
|
|
* useful to assign the data in derived classes.
|
||
|
|
*/
|
||
|
|
SharedAsset& operator=(const SharedAsset& rhs) {
|
||
|
|
CesiumUtility::ExtensibleObject::operator=(rhs);
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Assignment does not affect the asset's relationship with the depot, but is
|
||
|
|
* useful to assign the data in derived classes.
|
||
|
|
*/
|
||
|
|
SharedAsset& operator=(SharedAsset&& rhs) {
|
||
|
|
CesiumUtility::ExtensibleObject::operator=(std::move(rhs));
|
||
|
|
return *this;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
void addReference(bool threadOwnsDepotLock) const noexcept {
|
||
|
|
const int32_t prevReferences = this->_referenceCount++;
|
||
|
|
if (this->_pDepot && prevReferences <= 0) {
|
||
|
|
this->_pDepot->unmarkDeletionCandidate(
|
||
|
|
*static_cast<const T*>(this),
|
||
|
|
threadOwnsDepotLock);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void releaseReference(bool threadOwnsDepotLock) const noexcept {
|
||
|
|
CESIUM_ASSERT(this->_referenceCount > 0);
|
||
|
|
const int32_t references = --this->_referenceCount;
|
||
|
|
if (references == 0) {
|
||
|
|
IDepotOwningAsset<T>* pDepot = this->_pDepot;
|
||
|
|
if (pDepot) {
|
||
|
|
// Let the depot manage this object's lifetime.
|
||
|
|
pDepot->markDeletionCandidate(
|
||
|
|
*static_cast<const T*>(this),
|
||
|
|
threadOwnsDepotLock);
|
||
|
|
} else {
|
||
|
|
// No depot, so destroy this object directly.
|
||
|
|
delete static_cast<const T*>(this);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
mutable std::atomic<std::int32_t> _referenceCount{0};
|
||
|
|
IDepotOwningAsset<T>* _pDepot{nullptr};
|
||
|
|
|
||
|
|
// To allow the depot to modify _pDepot.
|
||
|
|
template <typename TAssetType, typename TAssetKey>
|
||
|
|
friend class CesiumAsync::SharedAssetDepot;
|
||
|
|
};
|
||
|
|
|
||
|
|
} // namespace CesiumUtility
|