From ce8cea357dac22ef7ae8915bcb5f907264fffb76 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 27 Nov 2018 23:50:38 +0700 Subject: [PATCH] basic content assets --- .gitattributes | 1 + headers/enduro2d/high/_all.hpp | 2 + headers/enduro2d/high/_high.hpp | 8 +- headers/enduro2d/high/assets.hpp | 38 ++++++++++ headers/enduro2d/high/library.hpp | 54 +++++++++++++- headers/enduro2d/high/library.inl | 95 ++++++++++++++++++++++++ sources/enduro2d/high/assets.cpp | 67 +++++++++++++++++ sources/enduro2d/high/library.cpp | 16 ++-- untests/bin/library/binary_asset.bin | 3 + untests/bin/library/text_asset.txt | 1 + untests/sources/untests_high/library.cpp | 65 ++++++++++++++++ 11 files changed, 341 insertions(+), 9 deletions(-) create mode 100644 headers/enduro2d/high/assets.hpp create mode 100644 headers/enduro2d/high/library.inl create mode 100644 sources/enduro2d/high/assets.cpp create mode 100644 untests/bin/library/binary_asset.bin create mode 100644 untests/bin/library/text_asset.txt diff --git a/.gitattributes b/.gitattributes index 486a2325..7dbbbeba 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ *.zip filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text diff --git a/headers/enduro2d/high/_all.hpp b/headers/enduro2d/high/_all.hpp index 14b656ce..5a2d63a0 100644 --- a/headers/enduro2d/high/_all.hpp +++ b/headers/enduro2d/high/_all.hpp @@ -8,4 +8,6 @@ #include "_high.hpp" +#include "assets.hpp" #include "library.hpp" +#include "library.inl" diff --git a/headers/enduro2d/high/_high.hpp b/headers/enduro2d/high/_high.hpp index 774ebffd..e478df95 100644 --- a/headers/enduro2d/high/_high.hpp +++ b/headers/enduro2d/high/_high.hpp @@ -6,9 +6,15 @@ #pragma once -#include "../core/_core.hpp" +#include "../core/_all.hpp" namespace e2d { + class asset; class library; + class text_asset; + class binary_asset; + + template < typename T > + class asset_cache; } diff --git a/headers/enduro2d/high/assets.hpp b/headers/enduro2d/high/assets.hpp new file mode 100644 index 00000000..5634d5d7 --- /dev/null +++ b/headers/enduro2d/high/assets.hpp @@ -0,0 +1,38 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018 Matvey Cherevko + ******************************************************************************/ + +#pragma once + +#include "_high.hpp" +#include "library.hpp" + +namespace e2d +{ + template < typename T > + class content_asset : public asset { + public: + content_asset(T content) + : content_(std::move(content)) {} + + const T& content() const noexcept { + return content_; + } + private: + T content_; + }; + + class text_asset final : public content_asset { + public: + using content_asset::content_asset; + static std::shared_ptr load(library& library, const url& url); + }; + + class binary_asset final : public content_asset { + public: + using content_asset::content_asset; + static std::shared_ptr load(library& library, const url& url); + }; +} diff --git a/headers/enduro2d/high/library.hpp b/headers/enduro2d/high/library.hpp index 9b3718e6..31570b0d 100644 --- a/headers/enduro2d/high/library.hpp +++ b/headers/enduro2d/high/library.hpp @@ -4,12 +4,18 @@ * Copyright (C) 2018 Matvey Cherevko ******************************************************************************/ +#ifndef E2D_INCLUDE_GUARD_C3A242D805B8481AB9F778BA8F272D72 +#define E2D_INCLUDE_GUARD_C3A242D805B8481AB9F778BA8F272D72 #pragma once #include "_high.hpp" namespace e2d { + // + // bad_library_operation + // + class bad_library_operation final : public exception { public: const char* what() const noexcept final { @@ -17,12 +23,54 @@ namespace e2d } }; + // + // asset + // + + class asset : private noncopyable { + public: + asset(); + virtual ~asset() noexcept; + }; + using asset_ptr = std::shared_ptr; + + // + // library + // + class library final : public module { public: - library(); + library(const url& root); ~library() noexcept final; + + template < typename T > + std::shared_ptr load_asset(str_view address); private: - class internal_state; - std::unique_ptr state_; + url root_; + }; + + // + // asset_cache + // + + template < typename T > + class asset_cache : public module> { + public: + asset_cache(library& l); + ~asset_cache() noexcept final; + + std::shared_ptr find(str_hash address) const; + void store(str_hash address, const std::shared_ptr& asset); + + void clear(); + void unload_unused_assets(); + std::size_t asset_count() const noexcept; + private: + library& library_; + mutable std::mutex mutex_; + hash_map> assets_; }; } + +#include "library.inl" +#endif diff --git a/headers/enduro2d/high/library.inl b/headers/enduro2d/high/library.inl new file mode 100644 index 00000000..f159173f --- /dev/null +++ b/headers/enduro2d/high/library.inl @@ -0,0 +1,95 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018 Matvey Cherevko + ******************************************************************************/ + +#ifndef E2D_INCLUDE_GUARD_C9B4A08B2E2A4A659AB390BDC6EBEFE8 +#define E2D_INCLUDE_GUARD_C9B4A08B2E2A4A659AB390BDC6EBEFE8 +#pragma once + +#include "_high.hpp" +#include "library.hpp" + +namespace e2d +{ + // + // library + // + + template < typename T > + std::shared_ptr library::load_asset(str_view address) { + const auto asset_url = root_ / address; + + if ( !modules::is_initialized>() ) { + return T::load(*this, asset_url); + } + + auto& cache = the>(); + + const auto cached_asset = cache.find(address); + if ( cached_asset ) { + return cached_asset; + } + + const auto new_asset = T::load(*this, asset_url); + if ( new_asset ) { + cache.store(address, new_asset); + return new_asset; + } + + return nullptr; + } + + // + // 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 > + std::shared_ptr asset_cache::find(str_hash address) const { + std::lock_guard guard(mutex_); + const auto iter = assets_.find(address); + return iter != assets_.end() + ? iter->second + : nullptr; + } + + template < typename T > + void asset_cache::store(str_hash address, const std::shared_ptr& asset) { + std::lock_guard guard(mutex_); + assets_[address] = asset; + } + + template < typename T > + void asset_cache::clear() { + std::lock_guard guard(mutex_); + assets_.clear(); + } + + template < typename T > + void asset_cache::unload_unused_assets() { + std::lock_guard guard(mutex_); + for ( auto iter = assets_.begin(); iter != assets_.end(); ) { + if ( iter->second.unique() ) { + iter = assets_.erase(iter); + } else { + ++iter; + } + } + } + + template < typename T > + std::size_t asset_cache::asset_count() const noexcept { + std::lock_guard guard(mutex_); + return assets_.size(); + } +} + +#endif diff --git a/sources/enduro2d/high/assets.cpp b/sources/enduro2d/high/assets.cpp new file mode 100644 index 00000000..6d418ec4 --- /dev/null +++ b/sources/enduro2d/high/assets.cpp @@ -0,0 +1,67 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018 Matvey Cherevko + ******************************************************************************/ + +#include + +namespace +{ + using namespace e2d; +} + +namespace e2d +{ + // + // text_asset + // + + std::shared_ptr text_asset::load(library& library, const url& url) { + E2D_UNUSED(library); + + input_stream_uptr stream = the().open(url); + if ( !stream ) { + the().error("ASSETS: Failed to open text asset file:\n" + "--> Url: %0", + url); + return nullptr; + } + + str content; + if ( !streams::try_read_tail(content, stream) ) { + the().error("ASSETS: Failed to read text asset file:\n" + "--> Url: %0", + url); + return nullptr; + } + + return std::make_shared(std::move(content)); + } + + // + // binary_asset + // + + std::shared_ptr binary_asset::load(library& library, const url& url) { + E2D_UNUSED(library); + + input_stream_uptr stream = the().open(url); + if ( !stream ) { + the().error("ASSETS: Failed to open binary asset file:\n" + "--> Url: %0", + url); + return nullptr; + } + + buffer content; + if ( !streams::try_read_tail(content, stream) ) { + the().error("ASSETS: Failed to read binary asset file:\n" + "--> Url: %0", + url); + return nullptr; + } + + return std::make_shared(std::move(content)); + } +} diff --git a/sources/enduro2d/high/library.cpp b/sources/enduro2d/high/library.cpp index afb6115d..60e9f399 100644 --- a/sources/enduro2d/high/library.cpp +++ b/sources/enduro2d/high/library.cpp @@ -6,20 +6,26 @@ #include +namespace +{ + using namespace e2d; +} + namespace e2d { // - // library::internal_state + // asset // - class library::internal_state final : private e2d::noncopyable { - }; + asset::asset() = default; + asset::~asset() noexcept = default; // // library // - library::library() - : state_(new internal_state()){} + library::library(const url& root) + : root_(root) {} + library::~library() noexcept = default; } diff --git a/untests/bin/library/binary_asset.bin b/untests/bin/library/binary_asset.bin new file mode 100644 index 00000000..17e2cda0 --- /dev/null +++ b/untests/bin/library/binary_asset.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:486ea46224d1bb4fb680f34f7c9ad96a8f24ec88be73ea8e5a6c65260e9cb8a7 +size 5 diff --git a/untests/bin/library/text_asset.txt b/untests/bin/library/text_asset.txt new file mode 100644 index 00000000..b6fc4c62 --- /dev/null +++ b/untests/bin/library/text_asset.txt @@ -0,0 +1 @@ +hello \ No newline at end of file diff --git a/untests/sources/untests_high/library.cpp b/untests/sources/untests_high/library.cpp index 2df1ecaf..34432675 100644 --- a/untests/sources/untests_high/library.cpp +++ b/untests/sources/untests_high/library.cpp @@ -8,4 +8,69 @@ using namespace e2d; TEST_CASE("library"){ + modules::initialize(); + modules::initialize(); + modules::initialize(v2u{640, 480}, "Enduro2D", false); + modules::initialize(the(), the()); + modules::initialize(url{"resources://bin/library"}); + modules::initialize>(the()); + modules::initialize>(the()); + { + the().register_sink(); + the().register_scheme("file"); + } + { + str resources; + REQUIRE(filesystem::extract_predef_path( + resources, + filesystem::predef_path::resources)); + REQUIRE(the().register_scheme_alias( + "resources", + {"file", resources})); + } + { + library& l = the(); + + auto text_res = l.load_asset("text_asset.txt"); + REQUIRE(text_res); + REQUIRE(text_res->content() == "hello"); + + auto binary_res = l.load_asset("binary_asset.bin"); + REQUIRE(binary_res); + REQUIRE(binary_res->content() == buffer("world", 5)); + + auto empty_res = l.load_asset("empty_asset"); + REQUIRE_FALSE(empty_res); + + auto binary_res_from_cache = l.load_asset("binary_asset.bin"); + REQUIRE(binary_res == binary_res_from_cache); + + REQUIRE(the>().asset_count() == 1); + REQUIRE(the>().asset_count() == 1); + + the>().unload_unused_assets(); + the>().unload_unused_assets(); + + REQUIRE(the>().asset_count() == 1); + REQUIRE(the>().asset_count() == 1); + + text_res.reset(); + the>().unload_unused_assets(); + REQUIRE(the>().asset_count() == 0); + REQUIRE(the>().asset_count() == 1); + + binary_res.reset(); + the>().unload_unused_assets(); + REQUIRE(the>().asset_count() == 1); + binary_res_from_cache.reset(); + the>().unload_unused_assets(); + REQUIRE(the>().asset_count() == 0); + } + modules::shutdown>(); + modules::shutdown>(); + modules::shutdown(); + modules::shutdown(); + modules::shutdown(); + modules::shutdown(); + modules::shutdown(); }