/* * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #pragma once #include #include #include #include #include namespace keel { class AssetManager; template class AssetRef; #ifndef OX_BARE_METAL template class AssetContainer { friend AssetManager; friend AssetRef; protected: ox::Signal updated; private: T m_obj; mutable int m_references = 0; public: template explicit constexpr AssetContainer(Args&&... args): m_obj(ox::forward(args)...) { } AssetContainer(AssetContainer const&) = delete; AssetContainer(AssetContainer&&) = delete; AssetContainer& operator=(AssetContainer const&) = delete; AssetContainer& operator=(AssetContainer&&) = delete; [[nodiscard]] constexpr T *get() noexcept { return &m_obj; } [[nodiscard]] constexpr const T *get() const noexcept { return &m_obj; } constexpr void set(T &&val) { m_obj = std::move(val); } constexpr void set(T const&val) { m_obj = val; } protected: constexpr void incRefs() const noexcept { ++m_references; } constexpr void decRefs() const noexcept { --m_references; } [[nodiscard]] constexpr int references() const noexcept { return m_references; } }; template class AssetRef: public ox::SignalHandler { private: const AssetContainer *m_ctr = nullptr; public: ox::Signal updated; explicit constexpr AssetRef(const AssetContainer *c = nullptr) noexcept; constexpr AssetRef(AssetRef const&h) noexcept; constexpr AssetRef(AssetRef &&h) noexcept; ~AssetRef() noexcept override { if (m_ctr) { m_ctr->decRefs(); } } [[nodiscard]] constexpr const T *get() const noexcept { if (m_ctr) { return m_ctr->get(); } return nullptr; } constexpr const T &operator*() const noexcept { return *m_ctr->get(); } constexpr const T *operator->() const noexcept { return m_ctr->get(); } AssetRef &operator=(AssetRef const&h) noexcept { if (this == &h) { return *this; } if (m_ctr) { m_ctr->decRefs(); oxIgnoreError(m_ctr->updated.disconnectObject(this)); } m_ctr = h.m_ctr; m_ctr->updated.connect(&updated, &ox::Signal::emitCheckError); if (m_ctr) { m_ctr->incRefs(); } return *this; } AssetRef &operator=(AssetRef &&h) noexcept { if (this == &h) { return *this; } if (m_ctr) { m_ctr->decRefs(); oxIgnoreError(m_ctr->updated.disconnectObject(this)); } m_ctr = h.m_ctr; m_ctr->updated.connect(this, &AssetRef::emitUpdated); h.m_ctr = nullptr; return *this; } explicit constexpr operator bool() const noexcept { return m_ctr; } private: constexpr ox::Error emitUpdated() const noexcept { updated.emit(); return OxError(0); } }; template constexpr AssetRef::AssetRef(const AssetContainer *c) noexcept: m_ctr(c) { if (c) { c->updated.connect(this, &AssetRef::emitUpdated); } } template constexpr AssetRef::AssetRef(AssetRef const&h) noexcept { m_ctr = h.m_ctr; if (m_ctr) { m_ctr->updated.connect(this, &AssetRef::emitUpdated); m_ctr->incRefs(); } } template constexpr AssetRef::AssetRef(AssetRef &&h) noexcept { m_ctr = h.m_ctr; m_ctr->updated.connect(this, &AssetRef::emitUpdated); h.m_ctr = nullptr; } class AssetManager { private: class AssetTypeManagerBase { public: virtual ~AssetTypeManagerBase() = default; virtual void gc() noexcept = 0; }; template class AssetTypeManager: public AssetTypeManagerBase { private: ox::HashMap>> m_cache; public: ox::Result> getAsset(ox::StringView const&assetId) const noexcept { auto out = m_cache.at(assetId); oxReturnError(out); return AssetRef(out.value->get()); } ox::Result> setAsset(ox::StringView const&assetId, const T &obj) noexcept { auto &p = m_cache[assetId]; if (!p) { p = ox::make_unique>(obj); } else { p->set(obj); p->updated.emit(); } return AssetRef(p.get()); } ox::Result> setAsset(ox::StringView const&assetId, T &&obj) noexcept { auto &p = m_cache[assetId]; if (!p) { p = ox::make_unique>(obj); } else { p->set(std::move(obj)); p->updated.emit(); } return AssetRef(p.get()); } void gc() noexcept final { for (const auto &ack : m_cache.keys()) { auto &ac = m_cache[ack]; if (!ac->references()) { m_cache.erase(ack); } } } }; ox::HashMap> m_assetTypeManagers; template AssetTypeManager *getTypeManager() noexcept { constexpr auto typeName = ox::requireModelTypeName(); static_assert(ox::StringView(typeName) != "", "Types must have TypeName to use AssetManager"); auto &am = m_assetTypeManagers[typeName]; if (!am) { am = ox::make_unique>(); } return dynamic_cast*>(am.get()); } public: template ox::Result> getAsset(ox::CRStringView assetId) noexcept { auto m = getTypeManager(); return m->getAsset(assetId); } template ox::Result> setAsset(ox::CRStringView assetId, const T &obj) noexcept { auto m = getTypeManager(); return m->setAsset(assetId, obj); } void gc() noexcept { for (auto const&amk : m_assetTypeManagers.keys()) { auto &am = m_assetTypeManagers[amk]; am->gc(); } } }; #else template class AssetRef { private: const T *const m_obj = nullptr; public: constexpr AssetRef() noexcept = default; explicit constexpr AssetRef(const T *obj) noexcept: m_obj(obj) { } constexpr const T *get() const noexcept { return m_obj; } constexpr const T &operator*() const & noexcept { return *m_obj; } constexpr const T &&operator*() const && noexcept { return *m_obj; } constexpr const T *operator->() const noexcept { return m_obj; } explicit constexpr operator bool() const noexcept { return m_obj; } }; #endif }