Merge branch 'feature/asset_dependencies' into feature/gobject

This commit is contained in:
2019-04-19 02:12:57 +07:00
13 changed files with 681 additions and 322 deletions

View File

@@ -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;

View File

@@ -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<asset> {
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<T> 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<asset_cache_base*> caches_;
hash_map<str_hash, asset_ptr> assets_;
};
//
// asset_cache
//
template < typename Asset >
class asset_cache : public asset_cache_base
, public module<asset_cache<Asset>> {
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<str_hash, asset_ptr> assets_;
};
//
// asset_factory
//
class asset_factory : public module<asset_factory> {
public:
using asset_creator = std::function<
stdex::promise<asset_ptr>(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<str_hash, asset_creator> creators_;
using asset_cache_uptr = std::unique_ptr<asset_cache_base>;
hash_map<utils::type_family_id, asset_cache_uptr> caches_;
};
}

View File

@@ -56,33 +56,29 @@ namespace e2d
}
template < typename Asset, typename Content >
template < typename T >
intrusive_ptr<T> content_asset<Asset, Content>::find_nested_asset(str_view name) const noexcept {
return dynamic_pointer_cast<T>(find_nested_asset(name));
template < typename NestedAsset >
typename NestedAsset::ptr content_asset<Asset, Content>::find_nested_asset(str_view nested_address) const noexcept {
return dynamic_pointer_cast<NestedAsset>(find_nested_asset(nested_address));
}
template < typename Asset, typename Content >
asset_ptr content_asset<Asset, Content>::find_nested_asset(str_view name) const noexcept {
const auto iter = nested_content_.find(name);
return iter != nested_content_.end()
asset_ptr content_asset<Asset, Content>::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<T>::asset_cache(library& l)
: library_(l) {}
template < typename T >
asset_cache<T>::~asset_cache() noexcept = default;
template < typename T >
typename asset_cache<T>::asset_ptr asset_cache<T>::find(str_hash address) const noexcept {
std::lock_guard<std::mutex> guard(mutex_);
typename typed_asset_cache<T>::asset_ptr typed_asset_cache<T>::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<T>::store(str_hash address, const asset_ptr& asset) {
std::lock_guard<std::mutex> guard(mutex_);
void typed_asset_cache<T>::store(str_hash address, const asset_ptr& asset) {
assets_[address] = asset;
}
template < typename T >
void asset_cache<T>::clear() noexcept {
std::lock_guard<std::mutex> guard(mutex_);
assets_.clear();
}
template < typename T >
std::size_t asset_cache<T>::asset_count() const noexcept {
std::lock_guard<std::mutex> guard(mutex_);
std::size_t typed_asset_cache<T>::asset_count() const noexcept {
return assets_.size();
}
template < typename T >
std::size_t asset_cache<T>::unload_self_unused_assets() noexcept {
std::lock_guard<std::mutex> guard(mutex_);
std::size_t typed_asset_cache<T>::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<asset>(result);
});
});
void asset_cache::store(str_hash address, const typename Asset::ptr& asset) {
const auto family = utils::type_family<Asset>::id();
const auto iter = caches_.find(family);
typed_asset_cache<Asset>* cache = iter != caches_.end() && iter->second
? static_cast<typed_asset_cache<Asset>*>(iter->second.get())
: nullptr;
if ( !cache ) {
cache = static_cast<typed_asset_cache<Asset>*>(caches_.emplace(
family,
std::make_unique<typed_asset_cache<Asset>>()).first->second.get());
}
cache->store(address, asset);
}
inline asset_factory& asset_factory::register_creator(str_hash type, asset_creator creator) {
std::lock_guard<std::mutex> 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<Asset>::id());
const typed_asset_cache<Asset>* cache = iter != caches_.end() && iter->second
? static_cast<const typed_asset_cache<Asset>*>(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<Asset>::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;
});
}
}

View File

@@ -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<loading_asset_base>;
class loading_asset_base
: private noncopyable
, public ref_counter<loading_asset_base> {
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<loading_asset>;
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<library> {
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<loading_asset_base_iptr>::iterator
find_loading_asset_iter_(str_hash address) const noexcept;
template < typename Asset >
typename loading_asset<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<bool> cancelled_{false};
private:
mutable asset_cache cache_;
mutable std::recursive_mutex mutex_;
mutable vector<loading_asset_base_iptr> 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<std::pair<str, asset_ptr>> assets_;
};
//
// asset_dependency_base
//
class asset_dependency_base;
using asset_dependency_base_iptr = intrusive_ptr<asset_dependency_base>;
class asset_dependency_base
: private noncopyable
, public ref_counter<asset_dependency_base> {
public:
asset_dependency_base() = default;
virtual ~asset_dependency_base() noexcept = default;
virtual const str& main_address() const noexcept = 0;
virtual stdex::promise<asset_ptr> 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<asset_ptr> 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<asset_group> load_async(const library& library);
private:
vector<asset_dependency_base_iptr> dependencies_;
};
}

View File

@@ -13,74 +13,277 @@
namespace e2d
{
//
// loading_asset
//
template < typename Asset >
loading_asset<Asset>::loading_asset(str_hash address, promise_type promise)
: address_(address)
, promise_(std::move(promise)) {}
template < typename Asset >
void loading_asset<Asset>::cancel() noexcept {
promise_.reject(library_cancelled_exception());
}
template < typename Asset >
str_hash loading_asset<Asset>::address() const noexcept {
return address_;
}
template < typename Asset >
const typename loading_asset<Asset>::promise_type& loading_asset<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<std::recursive_mutex> 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<std::recursive_mutex> 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<Asset>(address);
if ( modules::is_initialized<deferrer>() ) {
the<deferrer>().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<asset_cache<Asset>>() ) {
return Asset::load_async(*this, main_address);
if ( cancelled_ ) {
return stdex::make_rejected_promise<typename Asset::load_result>(library_cancelled_exception());
}
auto& cache = the<asset_cache<Asset>>();
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<std::recursive_mutex> guard(mutex_);
if ( auto cached_asset = cache_.find<Asset>(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_<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<std::recursive_mutex> guard(mutex_);
cache_.store<Asset>(main_address_hash, new_asset);
remove_loading_asset_<Asset>(main_address_hash);
return new_asset;
}).except([
this,
main_address_hash
](std::exception_ptr e) -> typename Asset::load_result {
std::lock_guard<std::recursive_mutex> guard(mutex_);
remove_loading_asset_<Asset>(main_address_hash);
std::rethrow_exception(e);
});
loading_assets_.push_back(new loading_asset<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<Asset, Nested>(address);
if ( modules::is_initialized<deferrer>() ) {
the<deferrer>().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<Asset>(address::parent(address))
return load_main_asset_async<Asset>(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<Nested>(main_asset)
: main_asset->template find_nested_asset<Nested>(nested_address);
if ( nested_asset ) {
return nested_asset;
}
using nested_asset_type = typename Nested::asset_type;
if ( auto result = dynamic_pointer_cast<nested_asset_type>(nested_asset) ) {
return result;
}
throw asset_loading_exception();
});
}
template < typename Asset >
vector<loading_asset_base_iptr>::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<loading_asset<Asset>>(asset);
});
}
template < typename Asset >
typename loading_asset<Asset>::ptr
library::find_loading_asset_(str_hash address) const noexcept {
auto iter = find_loading_asset_iter_<Asset>(address);
return iter != loading_assets_.end()
? static_pointer_cast<loading_asset<Asset>>(*iter)
: nullptr;
}
template < typename Asset >
void library::remove_loading_asset_(str_hash address) const noexcept {
auto iter = find_loading_asset_iter_<Asset>(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<Asset>(main_asset);
if ( !typed_main_asset ) {
continue;
}
typename Nested::load_result nested_asset = nested_address.empty()
? dynamic_pointer_cast<Nested>(typed_main_asset)
: typed_main_asset->template find_nested_asset<Nested>(nested_address);
if ( nested_asset ) {
return nested_asset;
}
}
return nullptr;
}
//
// asset_dependency
//
template < typename Asset >
asset_dependency<Asset>::asset_dependency(str_view address)
: main_address_(address::parent(address)) {}
template < typename Asset >
asset_dependency<Asset>::~asset_dependency() noexcept = default;
template < typename Asset >
const str& asset_dependency<Asset>::main_address() const noexcept {
return main_address_;
}
template < typename Asset >
stdex::promise<asset_ptr> asset_dependency<Asset>::load_async(const library& library) {
return library.load_main_asset_async<Asset>(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<Asset>(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_group> asset_dependencies::load_async(const library& library) {
vector<stdex::promise<std::pair<str, asset_ptr>>> 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<std::pair<str, asset_ptr>>& results){
return asset_group()
.add_assets(results.begin(), results.end());
});
}
}
#endif

View File

@@ -39,13 +39,13 @@ namespace e2d
namespace e2d
{
using str = basic_string<char>;
using wstr = basic_string<wchar_t>;
using str = basic_string<char>;
using wstr = basic_string<wchar_t>;
using str16 = basic_string<char16_t>;
using str32 = basic_string<char32_t>;
using str_view = basic_string_view<char>;
using wstr_view = basic_string_view<wchar_t>;
using str_view = basic_string_view<char>;
using wstr_view = basic_string_view<wchar_t>;
using str16_view = basic_string_view<char16_t>;
using str32_view = basic_string_view<char32_t>;
@@ -147,4 +147,36 @@ namespace e2d { namespace utils
constexpr std::underlying_type_t<E> enum_to_underlying(E e) noexcept {
return static_cast<std::underlying_type_t<E>>(e);
}
//
// type_family
//
using type_family_id = u32;
namespace impl
{
template < typename Void = void >
class type_family_base {
static_assert(
std::is_void<Void>::value &&
std::is_unsigned<type_family_id>::value,
"unexpected internal error");
protected:
static type_family_id last_id_;
};
template < typename Void >
type_family_id type_family_base<Void>::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;
}
};
}}

15
scripts/pvs_analyze.sh Executable file
View File

@@ -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

View File

@@ -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 <enduro2d/high/asset.hpp>
namespace e2d
{
//
// asset
//
asset::asset() = default;
asset::~asset() noexcept = default;
//
// asset_cache_base
//
std::mutex asset_cache_base::mutex_;
hash_set<asset_cache_base*> asset_cache_base::caches_;
asset_cache_base::asset_cache_base() {
std::lock_guard<std::mutex> guard(mutex_);
caches_.insert(this);
}
asset_cache_base::~asset_cache_base() noexcept {
std::lock_guard<std::mutex> guard(mutex_);
caches_.erase(this);
}
std::size_t asset_cache_base::unload_all_unused_assets() noexcept {
std::lock_guard<std::mutex> 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;
}

View File

@@ -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 <enduro2d/high/library.hpp>
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();
}
}

View File

@@ -9,20 +9,6 @@
#include <enduro2d/high/world.hpp>
#include <enduro2d/high/library.hpp>
#include <enduro2d/high/assets/atlas_asset.hpp>
#include <enduro2d/high/assets/binary_asset.hpp>
#include <enduro2d/high/assets/flipbook_asset.hpp>
#include <enduro2d/high/assets/image_asset.hpp>
#include <enduro2d/high/assets/material_asset.hpp>
#include <enduro2d/high/assets/mesh_asset.hpp>
#include <enduro2d/high/assets/model_asset.hpp>
#include <enduro2d/high/assets/prefab_asset.hpp>
#include <enduro2d/high/assets/shader_asset.hpp>
#include <enduro2d/high/assets/shape_asset.hpp>
#include <enduro2d/high/assets/sprite_asset.hpp>
#include <enduro2d/high/assets/text_asset.hpp>
#include <enduro2d/high/assets/texture_asset.hpp>
#include <enduro2d/high/systems/flipbook_system.hpp>
#include <enduro2d/high/systems/render_system.hpp>
@@ -129,53 +115,12 @@ namespace e2d
starter::starter(int argc, char *argv[], const parameters& params) {
safe_module_initialize<engine>(argc, argv, params.engine_params());
safe_module_initialize<library>(params.library_root());
safe_module_initialize<asset_cache<atlas_asset>>(the<library>());
safe_module_initialize<asset_cache<binary_asset>>(the<library>());
safe_module_initialize<asset_cache<flipbook_asset>>(the<library>());
safe_module_initialize<asset_cache<image_asset>>(the<library>());
safe_module_initialize<asset_cache<material_asset>>(the<library>());
safe_module_initialize<asset_cache<mesh_asset>>(the<library>());
safe_module_initialize<asset_cache<model_asset>>(the<library>());
safe_module_initialize<asset_cache<prefab_asset>>(the<library>());
safe_module_initialize<asset_cache<shader_asset>>(the<library>());
safe_module_initialize<asset_cache<shape_asset>>(the<library>());
safe_module_initialize<asset_cache<sprite_asset>>(the<library>());
safe_module_initialize<asset_cache<text_asset>>(the<library>());
safe_module_initialize<asset_cache<texture_asset>>(the<library>());
safe_module_initialize<asset_factory>()
.register_asset<atlas_asset>("atlas")
.register_asset<binary_asset>("binary")
.register_asset<flipbook_asset>("flipbook")
.register_asset<image_asset>("image")
.register_asset<material_asset>("material")
.register_asset<mesh_asset>("mesh")
.register_asset<model_asset>("model")
.register_asset<prefab_asset>("prefab")
.register_asset<shader_asset>("shader")
.register_asset<shape_asset>("shape")
.register_asset<sprite_asset>("sprite")
.register_asset<text_asset>("text")
.register_asset<texture_asset>("texture");
safe_module_initialize<library>(params.library_root(), the<deferrer>());
safe_module_initialize<world>();
}
starter::~starter() noexcept {
modules::shutdown<world>();
modules::shutdown<asset_factory>();
modules::shutdown<asset_cache<texture_asset>>();
modules::shutdown<asset_cache<text_asset>>();
modules::shutdown<asset_cache<sprite_asset>>();
modules::shutdown<asset_cache<shape_asset>>();
modules::shutdown<asset_cache<shader_asset>>();
modules::shutdown<asset_cache<prefab_asset>>();
modules::shutdown<asset_cache<model_asset>>();
modules::shutdown<asset_cache<mesh_asset>>();
modules::shutdown<asset_cache<material_asset>>();
modules::shutdown<asset_cache<image_asset>>();
modules::shutdown<asset_cache<flipbook_asset>>();
modules::shutdown<asset_cache<binary_asset>>();
modules::shutdown<asset_cache<atlas_asset>>();
modules::shutdown<library>();
modules::shutdown<engine>();
}

View File

@@ -44,36 +44,43 @@ namespace
};
}
TEST_CASE("asset"){
TEST_CASE("asset") {
safe_starter_initializer initializer;
library& l = the<library>();
{
REQUIRE_FALSE(l.load_asset<fake_asset>("none"));
SECTION("nested_assets") {
{
REQUIRE_FALSE(l.load_asset<fake_asset>("none"));
auto fa = l.load_asset<fake_asset>("42");
REQUIRE(fa);
REQUIRE(fa->content() == 42);
auto fa = l.load_asset<fake_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<binary_asset>("21"));
REQUIRE(fa->find_nested_asset<fake_nested_asset>("21"));
REQUIRE(fa->find_nested_asset<fake_nested_asset>("21")->content() == 21);
REQUIRE(fa->find_nested_asset<fake_nested_asset>("84")->content() == 84);
}
{
REQUIRE(l.load_asset<fake_asset, fake_nested_asset>("42:/21"));
REQUIRE(l.load_asset<fake_asset, fake_nested_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<binary_asset>("21"));
REQUIRE_FALSE(fa->find_nested_asset<binary_asset>("21:/2"));
REQUIRE(fa->find_nested_asset<fake_nested_asset>("21"));
REQUIRE(fa->find_nested_asset<fake_nested_asset>("21:/2"));
REQUIRE(fa->find_nested_asset<fake_nested_asset>("21")->content() == 21);
REQUIRE(fa->find_nested_asset<fake_nested_asset>("21:/2")->content() == 2);
REQUIRE(fa->find_nested_asset<fake_nested_asset>("84")->content() == 84);
REQUIRE(fa->find_nested_asset<fake_nested_asset>("84:/8")->content() == 8);
}
{
REQUIRE(l.load_asset<fake_asset, fake_nested_asset>("42:/21"));
REQUIRE(l.load_asset<fake_asset, fake_nested_asset>("42:/21")->content() == 21);
REQUIRE_FALSE(l.load_asset<fake_asset, binary_asset>("42:/21"));
REQUIRE_FALSE(l.load_asset<fake_asset, fake_nested_asset>("42:/none"));
REQUIRE_FALSE(l.load_asset<fake_asset, fake_nested_asset>("42:/none:/21"));
REQUIRE_FALSE(l.load_asset<fake_asset, binary_asset>("42:/21"));
REQUIRE_FALSE(l.load_asset<fake_asset, fake_nested_asset>("42:/none"));
REQUIRE_FALSE(l.load_asset<fake_asset, fake_nested_asset>("42:/none:/21"));
REQUIRE(l.load_asset<fake_asset, fake_nested_asset>("42:/21:/2"));
REQUIRE(l.load_asset<fake_asset, fake_nested_asset>("42:/21:/2")->content() == 2);
REQUIRE(l.load_asset<fake_asset, fake_nested_asset>("42:/21:/2"));
REQUIRE(l.load_asset<fake_asset, fake_nested_asset>("42:/21:/2")->content() == 2);
REQUIRE_FALSE(l.load_asset<fake_asset, binary_asset>("42:/21:/2"));
REQUIRE_FALSE(l.load_asset<fake_asset, fake_nested_asset>("42:/21:/none"));
REQUIRE_FALSE(l.load_asset<fake_asset, fake_nested_asset>("42:/21:/none:/2"));
REQUIRE_FALSE(l.load_asset<fake_asset, binary_asset>("42:/21:/2"));
REQUIRE_FALSE(l.load_asset<fake_asset, fake_nested_asset>("42:/21:/none"));
REQUIRE_FALSE(l.load_asset<fake_asset, fake_nested_asset>("42:/21:/none:/2"));
}
}
}

View File

@@ -27,6 +27,44 @@ namespace
TEST_CASE("library"){
safe_starter_initializer initializer;
library& l = the<library>();
{
binary_asset::ptr b1;
binary_asset::ptr b2;
{
auto p1 = l.load_asset_async<binary_asset>("binary_asset.bin");
auto p2 = l.load_asset_async<binary_asset>("binary_asset.bin");
the<deferrer>().active_safe_wait_promise(p1);
the<deferrer>().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<binary_asset>() == 0);
}
{
{
auto p1 = l.load_asset_async<binary_asset>("binary_asset.bin");
REQUIRE(l.loading_asset_count() == 1);
the<deferrer>().active_safe_wait_promise(p1);
REQUIRE(l.loading_asset_count() == 0);
auto p2 = l.load_asset_async<binary_asset>("none_asset");
REQUIRE(l.loading_asset_count() == 1);
the<deferrer>().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>("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<asset_cache<text_asset>>().unload_self_unused_assets());
REQUIRE(the<asset_cache<text_asset>>().asset_count() == 1);
REQUIRE(0u == l.unload_unused_assets());
REQUIRE(l.cache().asset_count() == 1);
REQUIRE(l.cache().asset_count<text_asset>() == 1);
text_res.reset();
text_res_from_cache.reset();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
REQUIRE(1u == the<asset_cache<text_asset>>().unload_self_unused_assets());
REQUIRE(the<asset_cache<text_asset>>().asset_count() == 0);
REQUIRE(1u == l.unload_unused_assets());
REQUIRE(l.cache().asset_count() == 0);
REQUIRE(l.cache().asset_count<text_asset>() == 0);
}
{
auto text_res = l.load_asset<text_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<binary_asset>("empty_asset");
@@ -72,19 +114,19 @@ TEST_CASE("library"){
REQUIRE(image_res);
REQUIRE(!image_res->content().empty());
REQUIRE(the<asset_cache<image_asset>>().find("image.png"));
REQUIRE(the<asset_cache<binary_asset>>().find("image.png"));
REQUIRE(l.cache().find<image_asset>("image.png"));
REQUIRE(l.cache().find<binary_asset>("image.png"));
std::this_thread::sleep_for(std::chrono::milliseconds(10));
the<asset_cache<binary_asset>>().unload_self_unused_assets();
REQUIRE(the<asset_cache<image_asset>>().find("image.png"));
REQUIRE_FALSE(the<asset_cache<binary_asset>>().find("image.png"));
l.unload_unused_assets();
REQUIRE(l.cache().find<image_asset>("image.png"));
REQUIRE_FALSE(l.cache().find<binary_asset>("image.png"));
image_res.reset();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
the<asset_cache<image_asset>>().unload_self_unused_assets();
REQUIRE_FALSE(the<asset_cache<image_asset>>().find("image.png"));
REQUIRE_FALSE(the<asset_cache<binary_asset>>().find("image.png"));
l.unload_unused_assets();
REQUIRE_FALSE(l.cache().find<image_asset>("image.png"));
REQUIRE_FALSE(l.cache().find<binary_asset>("image.png"));
}
{
if ( modules::is_initialized<render>() ) {
@@ -284,3 +326,39 @@ TEST_CASE("library"){
}
}
}
TEST_CASE("asset_dependencies") {
safe_starter_initializer initializer;
library& l = the<library>();
{
auto ad = asset_dependencies()
.add_dependency<text_asset>("text_asset.txt")
.add_dependency<binary_asset>("binary_asset.bin");
auto g1_p = ad.load_async(l);
the<deferrer>().active_safe_wait_promise(g1_p);
asset_group g1 = g1_p.get();
REQUIRE(g1.find_asset<text_asset>("text_asset.txt"));
REQUIRE(g1.find_asset<binary_asset>("binary_asset.bin"));
ad.add_dependency<text_asset>("none_asset");
auto g2_p = ad.load_async(l);
the<deferrer>().active_safe_wait_promise(g2_p);
}
{
if ( modules::is_initialized<render>() ) {
auto ad = asset_dependencies()
.add_dependency<atlas_asset>("atlas.json:/sprite");
auto g1_p = ad.load_async(l);
the<deferrer>().active_safe_wait_promise(g1_p);
asset_group g1 = g1_p.get();
REQUIRE(g1.find_asset<atlas_asset>("atlas.json")
== l.load_asset<atlas_asset>("atlas.json"));
REQUIRE(g1.find_asset<atlas_asset, sprite_asset>("atlas.json:/sprite")
== l.load_asset<atlas_asset, sprite_asset>("atlas.json:/sprite"));
ad.add_dependency<sprite_asset>("atlas.json:/sprite");
auto g2_p = ad.load_async(l);
the<deferrer>().active_safe_wait_promise(g2_p);
}
}
}

View File

@@ -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<str16>::id();
utils::type_family_id id2 = utils::type_family<str32>::id();
REQUIRE(id1 != id2);
REQUIRE(id1 == utils::type_family<str16>::id());
REQUIRE(id2 == utils::type_family<str32>::id());
}
}