diff --git a/headers/enduro2d/high/_high.hpp b/headers/enduro2d/high/_high.hpp index ebc25fb5..bf7d0ca0 100644 --- a/headers/enduro2d/high/_high.hpp +++ b/headers/enduro2d/high/_high.hpp @@ -50,11 +50,10 @@ namespace e2d class content_asset; class asset; - template < typename T > - class asset_cache; - class asset_cache_base; - class library; + class asset_cache; + class asset_group; + class asset_dependencies; class atlas; class flipbook; diff --git a/headers/enduro2d/high/asset.hpp b/headers/enduro2d/high/asset.hpp index 4a3a6d60..cc8bdd8d 100644 --- a/headers/enduro2d/high/asset.hpp +++ b/headers/enduro2d/high/asset.hpp @@ -10,6 +10,8 @@ #include "_high.hpp" +#include "address.hpp" + namespace e2d { // @@ -22,17 +24,6 @@ namespace e2d } }; - // - // bad_asset_factory_operation - // - - class bad_asset_factory_operation final : public exception { - public: - const char* what() const noexcept final { - return "bad asset factory operation"; - } - }; - // // asset // @@ -45,9 +36,9 @@ namespace e2d : private noncopyable , public ref_counter { public: - asset(); - virtual ~asset() noexcept; - virtual asset_ptr find_nested_asset(str_view name) const noexcept = 0; + asset() = default; + virtual ~asset() noexcept = default; + virtual asset_ptr find_nested_asset(str_view nested_address) const noexcept = 0; }; // @@ -73,9 +64,9 @@ namespace e2d const Content& content() const noexcept; - template < typename T > - intrusive_ptr find_nested_asset(str_view name) const noexcept; - asset_ptr find_nested_asset(str_view name) const noexcept override; + template < typename NestedAsset > + typename NestedAsset::ptr find_nested_asset(str_view nested_address) const noexcept; + asset_ptr find_nested_asset(str_view nested_address) const noexcept override; private: Content content_; nested_content nested_content_; @@ -87,60 +78,57 @@ namespace e2d class asset_cache_base : private noncopyable { public: - asset_cache_base(); - virtual ~asset_cache_base() noexcept; + asset_cache_base() = default; + virtual ~asset_cache_base() noexcept = default; - static std::size_t unload_all_unused_assets() noexcept; - virtual std::size_t unload_self_unused_assets() noexcept = 0; + virtual std::size_t asset_count() const noexcept = 0; + virtual std::size_t unload_unused_assets() noexcept = 0; + }; + + // + // typed_asset_cache + // + + template < typename Asset > + class typed_asset_cache : public asset_cache_base { + public: + using asset_ptr = typename Asset::ptr; + public: + typed_asset_cache() = default; + ~typed_asset_cache() noexcept final = default; + + asset_ptr find(str_hash address) const noexcept; + void store(str_hash address, const asset_ptr& asset); + + std::size_t asset_count() const noexcept override; + std::size_t unload_unused_assets() noexcept override; private: - static std::mutex mutex_; - static hash_set caches_; + hash_map assets_; }; // // asset_cache // - template < typename Asset > - class asset_cache : public asset_cache_base - , public module> { + class asset_cache final { public: - using asset_ptr = typename Asset::ptr; - public: - asset_cache(library& l); - ~asset_cache() noexcept final; - - asset_ptr find(str_hash address) const noexcept; - void store(str_hash address, const asset_ptr& asset); - - void clear() noexcept; - std::size_t asset_count() const noexcept; - - std::size_t unload_self_unused_assets() noexcept override; - private: - library& library_; - mutable std::mutex mutex_; - hash_map assets_; - }; - - // - // asset_factory - // - - class asset_factory : public module { - public: - using asset_creator = std::function< - stdex::promise(const library& library, str_view address)>; - public: - asset_factory(); - ~asset_factory() noexcept final; + asset_cache() = default; + ~asset_cache() noexcept = default; template < typename Asset > - asset_factory& register_asset(str_hash type); - asset_factory& register_creator(str_hash type, asset_creator creator); + void store(str_hash address, const typename Asset::ptr& asset); + + template < typename Asset > + typename Asset::ptr find(str_hash address) const noexcept; + + template < typename Asset > + std::size_t asset_count() const noexcept; + std::size_t asset_count() const noexcept; + + std::size_t unload_unused_assets() noexcept; private: - std::mutex mutex_; - hash_map creators_; + using asset_cache_uptr = std::unique_ptr; + hash_map caches_; }; } diff --git a/headers/enduro2d/high/asset.inl b/headers/enduro2d/high/asset.inl index 3f9ecf22..0a52d10c 100644 --- a/headers/enduro2d/high/asset.inl +++ b/headers/enduro2d/high/asset.inl @@ -56,33 +56,29 @@ namespace e2d } template < typename Asset, typename Content > - template < typename T > - intrusive_ptr content_asset::find_nested_asset(str_view name) const noexcept { - return dynamic_pointer_cast(find_nested_asset(name)); + template < typename NestedAsset > + typename NestedAsset::ptr content_asset::find_nested_asset(str_view nested_address) const noexcept { + return dynamic_pointer_cast(find_nested_asset(nested_address)); } template < typename Asset, typename Content > - asset_ptr content_asset::find_nested_asset(str_view name) const noexcept { - const auto iter = nested_content_.find(name); - return iter != nested_content_.end() + asset_ptr content_asset::find_nested_asset(str_view address) const noexcept { + const auto iter = nested_content_.find(make_hash(address::parent(address))); + if ( iter == nested_content_.end() ) { + return nullptr; + } + const str nested_asset = address::nested(address); + return nested_asset.empty() ? iter->second - : nullptr; + : iter->second->find_nested_asset(nested_asset); } // - // asset_cache + // typed_asset_cache // template < typename T > - asset_cache::asset_cache(library& l) - : library_(l) {} - - template < typename T > - asset_cache::~asset_cache() noexcept = default; - - template < typename T > - typename asset_cache::asset_ptr asset_cache::find(str_hash address) const noexcept { - std::lock_guard guard(mutex_); + typename typed_asset_cache::asset_ptr typed_asset_cache::find(str_hash address) const noexcept { const auto iter = assets_.find(address); return iter != assets_.end() ? iter->second @@ -90,26 +86,17 @@ namespace e2d } template < typename T > - void asset_cache::store(str_hash address, const asset_ptr& asset) { - std::lock_guard guard(mutex_); + void typed_asset_cache::store(str_hash address, const asset_ptr& asset) { assets_[address] = asset; } template < typename T > - void asset_cache::clear() noexcept { - std::lock_guard guard(mutex_); - assets_.clear(); - } - - template < typename T > - std::size_t asset_cache::asset_count() const noexcept { - std::lock_guard guard(mutex_); + std::size_t typed_asset_cache::asset_count() const noexcept { return assets_.size(); } template < typename T > - std::size_t asset_cache::unload_self_unused_assets() noexcept { - std::lock_guard guard(mutex_); + std::size_t typed_asset_cache::unload_unused_assets() noexcept { std::size_t result = 0u; for ( auto iter = assets_.begin(); iter != assets_.end(); ) { if ( !iter->second || 1 == iter->second->use_count() ) { @@ -123,27 +110,61 @@ namespace e2d } // - // asset_factory + // asset_cache // template < typename Asset > - asset_factory& asset_factory::register_asset(str_hash type) { - return register_creator(type, [](const library& library, str_view address) { - return Asset::load_async(library, address) - .then([](const typename Asset::load_result& result){ - return static_pointer_cast(result); - }); - }); + void asset_cache::store(str_hash address, const typename Asset::ptr& asset) { + const auto family = utils::type_family::id(); + const auto iter = caches_.find(family); + typed_asset_cache* cache = iter != caches_.end() && iter->second + ? static_cast*>(iter->second.get()) + : nullptr; + if ( !cache ) { + cache = static_cast*>(caches_.emplace( + family, + std::make_unique>()).first->second.get()); + } + cache->store(address, asset); } - inline asset_factory& asset_factory::register_creator(str_hash type, asset_creator creator) { - std::lock_guard guard(mutex_); - bool success = creators_.insert( - std::make_pair(type, std::move(creator))).second; - if ( !success ) { - throw bad_asset_factory_operation(); - } - return *this; + template < typename Asset > + typename Asset::ptr asset_cache::find(str_hash address) const noexcept { + const auto iter = caches_.find(utils::type_family::id()); + const typed_asset_cache* cache = iter != caches_.end() && iter->second + ? static_cast*>(iter->second.get()) + : nullptr; + return cache + ? cache->find(address) + : nullptr; + } + + template < typename Asset > + std::size_t asset_cache::asset_count() const noexcept { + const auto iter = caches_.find(utils::type_family::id()); + return iter != caches_.end() && iter->second + ? iter->second->asset_count() + : 0u; + } + + inline std::size_t asset_cache::asset_count() const noexcept { + return std::accumulate( + caches_.begin(), caches_.end(), std::size_t(0), + [](std::size_t acc, const auto& p){ + return p.second + ? acc + p.second->asset_count() + : acc; + }); + } + + inline std::size_t asset_cache::unload_unused_assets() noexcept { + return std::accumulate( + caches_.begin(), caches_.end(), std::size_t(0), + [](std::size_t acc, const auto& p){ + return p.second + ? acc + p.second->unload_unused_assets() + : acc; + }); } } diff --git a/headers/enduro2d/high/library.hpp b/headers/enduro2d/high/library.hpp index 9ec56e5e..29a6816d 100644 --- a/headers/enduro2d/high/library.hpp +++ b/headers/enduro2d/high/library.hpp @@ -11,32 +11,73 @@ #include "_high.hpp" #include "asset.hpp" -#include "address.hpp" namespace e2d { // - // bad_library_operation + // library_cancelled_exception // - class bad_library_operation final : public exception { - public: - const char* what() const noexcept final { - return "bad library operation"; + class library_cancelled_exception : public exception { + const char* what() const noexcept override { + return "library cancelled exception"; } }; + // + // loading_asset_base + // + + class loading_asset_base; + using loading_asset_base_iptr = intrusive_ptr; + + class loading_asset_base + : private noncopyable + , public ref_counter { + public: + loading_asset_base() = default; + virtual ~loading_asset_base() noexcept = default; + + virtual void cancel() noexcept = 0; + virtual str_hash address() const noexcept = 0; + }; + + // + // loading_asset + // + + template < typename Asset > + class loading_asset : public loading_asset_base { + public: + using ptr = intrusive_ptr; + using promise_type = typename Asset::load_async_result; + public: + loading_asset(str_hash address, promise_type promise); + ~loading_asset() noexcept override = default; + + void cancel() noexcept override; + str_hash address() const noexcept override; + + const promise_type& promise() const noexcept; + private: + str_hash address_; + promise_type promise_; + }; + // // library // class library final : public module { public: - library(const url& root); + library(const url& root, deferrer& deferrer); ~library() noexcept final; const url& root() const noexcept; + const asset_cache& cache() const noexcept; + std::size_t unload_unused_assets() noexcept; + std::size_t loading_asset_count() const noexcept; template < typename Asset > typename Asset::load_result load_main_asset(str_view address) const; @@ -49,8 +90,103 @@ namespace e2d template < typename Asset, typename Nested = Asset > typename Nested::load_async_result load_asset_async(str_view address) const; + private: + template < typename Asset > + vector::iterator + find_loading_asset_iter_(str_hash address) const noexcept; + + template < typename Asset > + typename loading_asset::ptr + find_loading_asset_(str_hash address) const noexcept; + + template < typename Asset > + void remove_loading_asset_(str_hash address) const noexcept; + + void cancel_all_loading_assets_() noexcept; private: url root_; + deferrer& deferrer_; + std::atomic cancelled_{false}; + private: + mutable asset_cache cache_; + mutable std::recursive_mutex mutex_; + mutable vector loading_assets_; + }; + + // + // asset_group + // + + class asset_group { + public: + asset_group() = default; + ~asset_group() noexcept = default; + + template < typename Iter > + asset_group& add_assets(Iter first, Iter last); + + template < typename Container > + asset_group& add_assets(Container&& container); + + asset_group& add_asset(str_view address, const asset_ptr& asset); + + template < typename Asset, typename Nested = Asset > + typename Nested::load_result find_asset(str_view address) const; + private: + vector> assets_; + }; + + // + // asset_dependency_base + // + + class asset_dependency_base; + using asset_dependency_base_iptr = intrusive_ptr; + + class asset_dependency_base + : private noncopyable + , public ref_counter { + public: + asset_dependency_base() = default; + virtual ~asset_dependency_base() noexcept = default; + + virtual const str& main_address() const noexcept = 0; + virtual stdex::promise load_async(const library& library) = 0; + }; + + // + // asset_dependency + // + + template < typename Asset > + class asset_dependency : public asset_dependency_base { + public: + using asset_type = Asset; + using load_result = typename Asset::load_result; + public: + asset_dependency(str_view address); + ~asset_dependency() noexcept override; + + const str& main_address() const noexcept override; + stdex::promise load_async(const library& library) override; + private: + str main_address_; + }; + + // + // asset_dependencies + // + + class asset_dependencies final { + public: + asset_dependencies() = default; + ~asset_dependencies() noexcept = default; + + template < typename Asset > + asset_dependencies& add_dependency(str_view address); + stdex::promise load_async(const library& library); + private: + vector dependencies_; }; } diff --git a/headers/enduro2d/high/library.inl b/headers/enduro2d/high/library.inl index 0c451ae4..f517dbd5 100644 --- a/headers/enduro2d/high/library.inl +++ b/headers/enduro2d/high/library.inl @@ -13,74 +13,277 @@ namespace e2d { + // + // loading_asset + // + + template < typename Asset > + loading_asset::loading_asset(str_hash address, promise_type promise) + : address_(address) + , promise_(std::move(promise)) {} + + template < typename Asset > + void loading_asset::cancel() noexcept { + promise_.reject(library_cancelled_exception()); + } + + template < typename Asset > + str_hash loading_asset::address() const noexcept { + return address_; + } + + template < typename Asset > + const typename loading_asset::promise_type& loading_asset::promise() const noexcept { + return promise_; + } + + // + // library + // + + inline library::library(const url& root, deferrer& deferrer) + : root_(root) + , deferrer_(deferrer) {} + + inline library::~library() noexcept { + std::unique_lock lock(mutex_); + cancelled_.store(true); + cancel_all_loading_assets_(); + } + + inline const url& library::root() const noexcept { + return root_; + } + + inline const asset_cache& library::cache() const noexcept { + return cache_; + } + + inline std::size_t library::unload_unused_assets() noexcept { + return cache_.unload_unused_assets(); + } + + inline std::size_t library::loading_asset_count() const noexcept { + std::lock_guard guard(mutex_); + return loading_assets_.size(); + } + template < typename Asset > typename Asset::load_result library::load_main_asset(str_view address) const { auto p = load_main_asset_async(address); - - if ( modules::is_initialized() ) { - the().active_safe_wait_promise(p); - } - + deferrer_.active_safe_wait_promise(p); return p.get_or_default(nullptr); } template < typename Asset > typename Asset::load_async_result library::load_main_asset_async(str_view address) const { - const auto main_address = address::parent(address); - const auto main_address_hash = make_hash(main_address); - - if ( !modules::is_initialized>() ) { - return Asset::load_async(*this, main_address); + if ( cancelled_ ) { + return stdex::make_rejected_promise(library_cancelled_exception()); } - auto& cache = the>(); - if ( auto cached_asset = cache.find(main_address_hash) ) { + const str main_address = address::parent(address); + const str_hash main_address_hash = make_hash(main_address); + + std::lock_guard guard(mutex_); + + if ( auto cached_asset = cache_.find(main_address_hash) ) { return stdex::make_resolved_promise(std::move(cached_asset)); } - return Asset::load_async(*this, main_address) - .then([ - &cache, - main_address_hash - ](const typename Asset::load_result& new_asset){ - cache.store(main_address_hash, new_asset); - return new_asset; - }); + if ( auto asset = find_loading_asset_(main_address_hash) ) { + return asset->promise(); + } + + auto p = Asset::load_async(*this, main_address) + .then([ + this, + main_address_hash + ](const typename Asset::load_result& new_asset){ + std::lock_guard guard(mutex_); + cache_.store(main_address_hash, new_asset); + remove_loading_asset_(main_address_hash); + return new_asset; + }).except([ + this, + main_address_hash + ](std::exception_ptr e) -> typename Asset::load_result { + std::lock_guard guard(mutex_); + remove_loading_asset_(main_address_hash); + std::rethrow_exception(e); + }); + + loading_assets_.push_back(new loading_asset(address, p)); + return p; } template < typename Asset, typename Nested > typename Nested::load_result library::load_asset(str_view address) const { auto p = load_asset_async(address); - - if ( modules::is_initialized() ) { - the().active_safe_wait_promise(p); - } - + deferrer_.active_safe_wait_promise(p); return p.get_or_default(nullptr); } template < typename Asset, typename Nested > typename Nested::load_async_result library::load_asset_async(str_view address) const { - return load_main_asset_async(address::parent(address)) + return load_main_asset_async(address) .then([ - address = str(address) + nested_address = address::nested(address) ](const typename Asset::load_result& main_asset){ - asset_ptr nested_asset = main_asset; - str nested_address = address::nested(address); - - while ( nested_asset && !nested_address.empty() ) { - nested_asset = nested_asset->find_nested_asset(address::parent(nested_address)); - nested_address = address::nested(nested_address); + typename Nested::load_result nested_asset = nested_address.empty() + ? dynamic_pointer_cast(main_asset) + : main_asset->template find_nested_asset(nested_address); + if ( nested_asset ) { + return nested_asset; } - - using nested_asset_type = typename Nested::asset_type; - if ( auto result = dynamic_pointer_cast(nested_asset) ) { - return result; - } - throw asset_loading_exception(); }); } + + template < typename Asset > + vector::iterator + library::find_loading_asset_iter_(str_hash address) const noexcept { + return std::find_if( + loading_assets_.begin(), loading_assets_.end(), + [address](const loading_asset_base_iptr& asset) noexcept { + return asset->address() == address + && dynamic_pointer_cast>(asset); + }); + } + + template < typename Asset > + typename loading_asset::ptr + library::find_loading_asset_(str_hash address) const noexcept { + auto iter = find_loading_asset_iter_(address); + return iter != loading_assets_.end() + ? static_pointer_cast>(*iter) + : nullptr; + } + + template < typename Asset > + void library::remove_loading_asset_(str_hash address) const noexcept { + auto iter = find_loading_asset_iter_(address); + if ( iter != loading_assets_.end() ) { + loading_assets_.erase(iter); + } + } + + inline void library::cancel_all_loading_assets_() noexcept { + for ( const auto& asset : loading_assets_ ) { + asset->cancel(); + } + loading_assets_.clear(); + } + + // + // asset_group + // + + template < typename Iter > + asset_group& asset_group::add_assets(Iter first, Iter last) { + for ( auto iter = first; iter != last; ++iter ) { + add_asset(iter->first, iter->second); + } + return *this; + } + + template < typename Container > + asset_group& asset_group::add_assets(Container&& container) { + return add_assets(std::begin(container), std::end(container)); + } + + inline asset_group& asset_group::add_asset(str_view address, const asset_ptr& asset) { + str main_address = address::parent(address); + auto iter = std::upper_bound( + assets_.begin(), assets_.end(), main_address, + [](const str& l, const auto& r){ + return l < r.first; + }); + assets_.insert(iter, std::make_pair(std::move(main_address), asset)); + return *this; + } + + template < typename Asset, typename Nested > + typename Nested::load_result asset_group::find_asset(str_view address) const { + const str main_address = address::parent(address); + const str nested_address = address::nested(address); + auto iter = std::lower_bound( + assets_.begin(), assets_.end(), main_address, + [](const auto& l, const str& r) noexcept { + return l.first < r; + }); + for ( ; iter != assets_.end() && iter->first == main_address; ++iter ) { + asset_ptr main_asset = iter->second; + typename Asset::load_result typed_main_asset = dynamic_pointer_cast(main_asset); + if ( !typed_main_asset ) { + continue; + } + typename Nested::load_result nested_asset = nested_address.empty() + ? dynamic_pointer_cast(typed_main_asset) + : typed_main_asset->template find_nested_asset(nested_address); + if ( nested_asset ) { + return nested_asset; + } + } + return nullptr; + } + + // + // asset_dependency + // + + template < typename Asset > + asset_dependency::asset_dependency(str_view address) + : main_address_(address::parent(address)) {} + + template < typename Asset > + asset_dependency::~asset_dependency() noexcept = default; + + template < typename Asset > + const str& asset_dependency::main_address() const noexcept { + return main_address_; + } + + template < typename Asset > + stdex::promise asset_dependency::load_async(const library& library) { + return library.load_main_asset_async(main_address()) + .then([](const typename Asset::load_result& main_asset){ + return asset_ptr(main_asset); + }); + } + + // + // asset_dependencies + // + + template < typename Asset > + asset_dependencies& asset_dependencies::add_dependency(str_view address) { + asset_dependency_base_iptr dep(new asset_dependency(address)); + auto iter = std::upper_bound( + dependencies_.begin(), dependencies_.end(), dep->main_address(), + [](const str& l, const auto& r){ + return l < r->main_address(); + }); + dependencies_.insert(iter, std::move(dep)); + return *this; + } + + inline stdex::promise asset_dependencies::load_async(const library& library) { + vector>> promises; + promises.reserve(dependencies_.size()); + std::transform( + dependencies_.begin(), dependencies_.end(), std::back_inserter(promises), + [&library](const asset_dependency_base_iptr& dep){ + return dep->load_async(library) + .then([main_address = dep->main_address()](const asset_ptr& asset){ + return std::make_pair(main_address, asset); + }); + }); + return stdex::make_all_promise(std::move(promises)) + .then([](const vector>& results){ + return asset_group() + .add_assets(results.begin(), results.end()); + }); + } } #endif diff --git a/headers/enduro2d/utils/_utils.hpp b/headers/enduro2d/utils/_utils.hpp index 61448008..d89ce9bf 100644 --- a/headers/enduro2d/utils/_utils.hpp +++ b/headers/enduro2d/utils/_utils.hpp @@ -39,13 +39,13 @@ namespace e2d namespace e2d { - using str = basic_string; - using wstr = basic_string; + using str = basic_string; + using wstr = basic_string; using str16 = basic_string; using str32 = basic_string; - using str_view = basic_string_view; - using wstr_view = basic_string_view; + using str_view = basic_string_view; + using wstr_view = basic_string_view; using str16_view = basic_string_view; using str32_view = basic_string_view; @@ -147,4 +147,36 @@ namespace e2d { namespace utils constexpr std::underlying_type_t enum_to_underlying(E e) noexcept { return static_cast>(e); } + + // + // type_family + // + + using type_family_id = u32; + + namespace impl + { + template < typename Void = void > + class type_family_base { + static_assert( + std::is_void::value && + std::is_unsigned::value, + "unexpected internal error"); + protected: + static type_family_id last_id_; + }; + + template < typename Void > + type_family_id type_family_base::last_id_ = 0u; + } + + template < typename T > + class type_family final : public impl::type_family_base<> { + public: + static type_family_id id() noexcept { + static type_family_id self_id = ++last_id_; + assert(self_id > 0u && "type_family_id overflow"); + return self_id; + } + }; }} diff --git a/scripts/pvs_analyze.sh b/scripts/pvs_analyze.sh new file mode 100755 index 00000000..501d2e5c --- /dev/null +++ b/scripts/pvs_analyze.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e +BUILD_DIR=`dirname "$BASH_SOURCE"`/../build +mkdir -p $BUILD_DIR/pvs_analyze +cd $BUILD_DIR/pvs_analyze +rm -rf pvs_report +cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=On ../.. +pvs-studio-analyzer analyze\ + -e ../../untests\ + -e ../../modules\ + -e ../../sources/3rdparty\ + -o pvs_report.log\ + -j8 +plog-converter -a GA:1,2 -t fullhtml -o pvs_report pvs_report.log +open pvs_report/index.html diff --git a/sources/enduro2d/high/asset.cpp b/sources/enduro2d/high/asset.cpp deleted file mode 100644 index 62eebf11..00000000 --- a/sources/enduro2d/high/asset.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* - * This file is part of the "Enduro2D" - * For conditions of distribution and use, see copyright notice in LICENSE.md - * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) - ******************************************************************************/ - -#include - -namespace e2d -{ - // - // asset - // - - asset::asset() = default; - asset::~asset() noexcept = default; - - // - // asset_cache_base - // - - std::mutex asset_cache_base::mutex_; - hash_set asset_cache_base::caches_; - - asset_cache_base::asset_cache_base() { - std::lock_guard guard(mutex_); - caches_.insert(this); - } - - asset_cache_base::~asset_cache_base() noexcept { - std::lock_guard guard(mutex_); - caches_.erase(this); - } - - std::size_t asset_cache_base::unload_all_unused_assets() noexcept { - std::lock_guard guard(mutex_); - return std::accumulate(caches_.begin(), caches_.end(), std::size_t(0), - [](std::size_t acc, asset_cache_base* cache){ - return acc + cache->unload_self_unused_assets(); - }); - } - - // - // asset_factory - // - - asset_factory::asset_factory() = default; - asset_factory::~asset_factory() noexcept = default; -} diff --git a/sources/enduro2d/high/library.cpp b/sources/enduro2d/high/library.cpp deleted file mode 100644 index 4a3573f0..00000000 --- a/sources/enduro2d/high/library.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * This file is part of the "Enduro2D" - * For conditions of distribution and use, see copyright notice in LICENSE.md - * Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) - ******************************************************************************/ - -#include - -namespace e2d -{ - library::library(const url& root) - : root_(root) {} - - library::~library() noexcept = default; - - const url& library::root() const noexcept { - return root_; - } - - std::size_t library::unload_unused_assets() noexcept { - return asset_cache_base::unload_all_unused_assets(); - } -} diff --git a/sources/enduro2d/high/starter.cpp b/sources/enduro2d/high/starter.cpp index 685db6ab..a9b8f8fe 100644 --- a/sources/enduro2d/high/starter.cpp +++ b/sources/enduro2d/high/starter.cpp @@ -9,20 +9,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include @@ -129,53 +115,12 @@ namespace e2d starter::starter(int argc, char *argv[], const parameters& params) { safe_module_initialize(argc, argv, params.engine_params()); - safe_module_initialize(params.library_root()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize>(the()); - safe_module_initialize() - .register_asset("atlas") - .register_asset("binary") - .register_asset("flipbook") - .register_asset("image") - .register_asset("material") - .register_asset("mesh") - .register_asset("model") - .register_asset("prefab") - .register_asset("shader") - .register_asset("shape") - .register_asset("sprite") - .register_asset("text") - .register_asset("texture"); + safe_module_initialize(params.library_root(), the()); safe_module_initialize(); } starter::~starter() noexcept { modules::shutdown(); - modules::shutdown(); - modules::shutdown>(); - modules::shutdown>(); - modules::shutdown>(); - modules::shutdown>(); - modules::shutdown>(); - modules::shutdown>(); - modules::shutdown>(); - modules::shutdown>(); - modules::shutdown>(); - modules::shutdown>(); - modules::shutdown>(); - modules::shutdown>(); - modules::shutdown>(); modules::shutdown(); modules::shutdown(); } diff --git a/untests/sources/untests_high/asset.cpp b/untests/sources/untests_high/asset.cpp index ce748d05..a3519dfe 100644 --- a/untests/sources/untests_high/asset.cpp +++ b/untests/sources/untests_high/asset.cpp @@ -44,36 +44,43 @@ namespace }; } -TEST_CASE("asset"){ +TEST_CASE("asset") { safe_starter_initializer initializer; library& l = the(); - { - REQUIRE_FALSE(l.load_asset("none")); + SECTION("nested_assets") { + { + REQUIRE_FALSE(l.load_asset("none")); - auto fa = l.load_asset("42"); - REQUIRE(fa); - REQUIRE(fa->content() == 42); + auto fa = l.load_asset("42"); + REQUIRE(fa); + REQUIRE(fa->content() == 42); - REQUIRE_FALSE(fa->find_nested_asset("none")); - REQUIRE(fa->find_nested_asset("21")); - REQUIRE_FALSE(fa->find_nested_asset("21")); - REQUIRE(fa->find_nested_asset("21")); - REQUIRE(fa->find_nested_asset("21")->content() == 21); - REQUIRE(fa->find_nested_asset("84")->content() == 84); - } - { - REQUIRE(l.load_asset("42:/21")); - REQUIRE(l.load_asset("42:/21")->content() == 21); + REQUIRE_FALSE(fa->find_nested_asset("none")); + REQUIRE(fa->find_nested_asset("21")); + REQUIRE(fa->find_nested_asset("21:/2")); + REQUIRE_FALSE(fa->find_nested_asset("21")); + REQUIRE_FALSE(fa->find_nested_asset("21:/2")); + REQUIRE(fa->find_nested_asset("21")); + REQUIRE(fa->find_nested_asset("21:/2")); + REQUIRE(fa->find_nested_asset("21")->content() == 21); + REQUIRE(fa->find_nested_asset("21:/2")->content() == 2); + REQUIRE(fa->find_nested_asset("84")->content() == 84); + REQUIRE(fa->find_nested_asset("84:/8")->content() == 8); + } + { + REQUIRE(l.load_asset("42:/21")); + REQUIRE(l.load_asset("42:/21")->content() == 21); - REQUIRE_FALSE(l.load_asset("42:/21")); - REQUIRE_FALSE(l.load_asset("42:/none")); - REQUIRE_FALSE(l.load_asset("42:/none:/21")); + REQUIRE_FALSE(l.load_asset("42:/21")); + REQUIRE_FALSE(l.load_asset("42:/none")); + REQUIRE_FALSE(l.load_asset("42:/none:/21")); - REQUIRE(l.load_asset("42:/21:/2")); - REQUIRE(l.load_asset("42:/21:/2")->content() == 2); + REQUIRE(l.load_asset("42:/21:/2")); + REQUIRE(l.load_asset("42:/21:/2")->content() == 2); - REQUIRE_FALSE(l.load_asset("42:/21:/2")); - REQUIRE_FALSE(l.load_asset("42:/21:/none")); - REQUIRE_FALSE(l.load_asset("42:/21:/none:/2")); + REQUIRE_FALSE(l.load_asset("42:/21:/2")); + REQUIRE_FALSE(l.load_asset("42:/21:/none")); + REQUIRE_FALSE(l.load_asset("42:/21:/none:/2")); + } } } diff --git a/untests/sources/untests_high/library.cpp b/untests/sources/untests_high/library.cpp index dab0b93f..25622971 100644 --- a/untests/sources/untests_high/library.cpp +++ b/untests/sources/untests_high/library.cpp @@ -27,6 +27,44 @@ namespace TEST_CASE("library"){ safe_starter_initializer initializer; library& l = the(); + { + binary_asset::ptr b1; + binary_asset::ptr b2; + + { + auto p1 = l.load_asset_async("binary_asset.bin"); + auto p2 = l.load_asset_async("binary_asset.bin"); + + the().active_safe_wait_promise(p1); + the().active_safe_wait_promise(p2); + + b1 = p1.get(); + b2 = p2.get(); + REQUIRE(b1 == b2); + } + + b1.reset(); + b2.reset(); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + REQUIRE(1u == l.unload_unused_assets()); + REQUIRE(l.cache().asset_count() == 0); + } + { + { + auto p1 = l.load_asset_async("binary_asset.bin"); + REQUIRE(l.loading_asset_count() == 1); + the().active_safe_wait_promise(p1); + REQUIRE(l.loading_asset_count() == 0); + + auto p2 = l.load_asset_async("none_asset"); + REQUIRE(l.loading_asset_count() == 1); + the().active_safe_wait_promise(p2); + REQUIRE(l.loading_asset_count() == 0); + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + REQUIRE(1u == l.unload_unused_assets()); + } { auto text_res = l.load_asset("text_asset.txt"); REQUIRE(text_res); @@ -36,15 +74,17 @@ TEST_CASE("library"){ REQUIRE(text_res_from_cache); REQUIRE(text_res_from_cache.get() == text_res.get()); - REQUIRE(0u == the>().unload_self_unused_assets()); - REQUIRE(the>().asset_count() == 1); + REQUIRE(0u == l.unload_unused_assets()); + REQUIRE(l.cache().asset_count() == 1); + REQUIRE(l.cache().asset_count() == 1); text_res.reset(); text_res_from_cache.reset(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); - REQUIRE(1u == the>().unload_self_unused_assets()); - REQUIRE(the>().asset_count() == 0); + REQUIRE(1u == l.unload_unused_assets()); + REQUIRE(l.cache().asset_count() == 0); + REQUIRE(l.cache().asset_count() == 0); } { auto text_res = l.load_asset("text_asset.txt"); @@ -56,12 +96,14 @@ TEST_CASE("library"){ REQUIRE(binary_res->content() == buffer("world", 5)); REQUIRE(0u == l.unload_unused_assets()); + REQUIRE(l.cache().asset_count() == 2); text_res.reset(); binary_res.reset(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); REQUIRE(2u == l.unload_unused_assets()); + REQUIRE(l.cache().asset_count() == 0); } { auto empty_res = l.load_asset("empty_asset"); @@ -72,19 +114,19 @@ TEST_CASE("library"){ REQUIRE(image_res); REQUIRE(!image_res->content().empty()); - REQUIRE(the>().find("image.png")); - REQUIRE(the>().find("image.png")); + REQUIRE(l.cache().find("image.png")); + REQUIRE(l.cache().find("image.png")); std::this_thread::sleep_for(std::chrono::milliseconds(10)); - the>().unload_self_unused_assets(); - REQUIRE(the>().find("image.png")); - REQUIRE_FALSE(the>().find("image.png")); + l.unload_unused_assets(); + REQUIRE(l.cache().find("image.png")); + REQUIRE_FALSE(l.cache().find("image.png")); image_res.reset(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); - the>().unload_self_unused_assets(); - REQUIRE_FALSE(the>().find("image.png")); - REQUIRE_FALSE(the>().find("image.png")); + l.unload_unused_assets(); + REQUIRE_FALSE(l.cache().find("image.png")); + REQUIRE_FALSE(l.cache().find("image.png")); } { if ( modules::is_initialized() ) { @@ -284,3 +326,39 @@ TEST_CASE("library"){ } } } + +TEST_CASE("asset_dependencies") { + safe_starter_initializer initializer; + library& l = the(); + { + auto ad = asset_dependencies() + .add_dependency("text_asset.txt") + .add_dependency("binary_asset.bin"); + auto g1_p = ad.load_async(l); + the().active_safe_wait_promise(g1_p); + asset_group g1 = g1_p.get(); + REQUIRE(g1.find_asset("text_asset.txt")); + REQUIRE(g1.find_asset("binary_asset.bin")); + + ad.add_dependency("none_asset"); + auto g2_p = ad.load_async(l); + the().active_safe_wait_promise(g2_p); + } + { + if ( modules::is_initialized() ) { + auto ad = asset_dependencies() + .add_dependency("atlas.json:/sprite"); + auto g1_p = ad.load_async(l); + the().active_safe_wait_promise(g1_p); + asset_group g1 = g1_p.get(); + REQUIRE(g1.find_asset("atlas.json") + == l.load_asset("atlas.json")); + REQUIRE(g1.find_asset("atlas.json:/sprite") + == l.load_asset("atlas.json:/sprite")); + + ad.add_dependency("atlas.json:/sprite"); + auto g2_p = ad.load_async(l); + the().active_safe_wait_promise(g2_p); + } + } +} diff --git a/untests/sources/untests_utils/_utils.cpp b/untests/sources/untests_utils/_utils.cpp index 95023e6f..99a43b7a 100644 --- a/untests/sources/untests_utils/_utils.cpp +++ b/untests/sources/untests_utils/_utils.cpp @@ -49,4 +49,11 @@ TEST_CASE("utils") { 42u, str1, str1 + std::strlen(str1) ) == utils::sdbm_hash(42u, str2)); } + { + utils::type_family_id id1 = utils::type_family::id(); + utils::type_family_id id2 = utils::type_family::id(); + REQUIRE(id1 != id2); + REQUIRE(id1 == utils::type_family::id()); + REQUIRE(id2 == utils::type_family::id()); + } }