From bd1393c505c721f7face1af80e194f47adec6419 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Mon, 3 Jun 2024 13:59:50 +0300 Subject: [PATCH] Improve working with ecs caches, support retrieving ecs cache & table count, and fix ecs query container memory leak --- libs/ecs/include/psemek/ecs/container.hpp | 48 +++++++---------- .../include/psemek/ecs/detail/query_cache.hpp | 6 ++- .../ecs/detail/query_cache_container.hpp | 53 +++++++++++-------- .../psemek/ecs/detail/table_container.hpp | 9 +++- libs/ecs/source/container.cpp | 10 ++++ libs/ecs/source/detail/query_cache.cpp | 8 +-- 6 files changed, 73 insertions(+), 61 deletions(-) diff --git a/libs/ecs/include/psemek/ecs/container.hpp b/libs/ecs/include/psemek/ecs/container.hpp index 04b7232f..ca715d4d 100644 --- a/libs/ecs/include/psemek/ecs/container.hpp +++ b/libs/ecs/include/psemek/ecs/container.hpp @@ -381,6 +381,10 @@ namespace psemek::ecs template std::size_t memory_usage(); + std::size_t cache_count(); + + std::size_t table_count(); + private: detail::entity_list entity_list_; detail::table_container table_container_; @@ -391,11 +395,6 @@ namespace psemek::ecs util::object_pool> uuid_list_pool_; util::object_pool> uuid_set_pool_; - std::vector callback_caches_; - - template - query_cache cache_impl(Constructor && constructor, Destructor && destructor); - detail::table * insert_table(std::vector> columns); void do_destroy(handle entity); void remove_row(detail::table & table, std::uint32_t row, util::span entities); @@ -563,7 +562,10 @@ namespace psemek::ecs template query_cache container::cache() { - return cache_impl(nullptr, nullptr); + typename detail::filter_with ...>>::type with_uuids; + typename detail::filter_without...>>::type without_uuids; + + return query_cache_container_.create(with_uuids.get(), without_uuids.get(), table_container_); } template @@ -642,7 +644,7 @@ namespace psemek::ecs static_assert(invocable_type::value, "function is not invocable with these components"); - auto constructor = [function = std::move(function)](std::vector const & column_indices) -> detail::table_callback { + auto constructor_factory = [function = std::move(function)](std::vector const & column_indices) -> detail::table_callback { return [function, column_indices](container & container, detail::table & table, std::uint32_t row, util::hash_set const & attached_components, bool force){ bool const invoke = force || (detail::contains_helper::contains(attached_components) || ...); @@ -659,9 +661,11 @@ namespace psemek::ecs }; }; - query_cache cache = this->cache_impl(std::move(constructor), nullptr); + query_cache cache = this->cache(); - callback_caches_.push_back(cache); + cache->constructor_factories.push_back(constructor_factory); + for (auto const & entry : cache->entries) + entry.table->add_constructor(constructor_factory(entry.columns_indices)); } template @@ -673,7 +677,7 @@ namespace psemek::ecs static_assert(invocable_type::value, "function is not invocable with these components"); - auto destructor = [function = std::move(function)](std::vector const & column_indices) -> detail::table_callback { + auto destructor_factory = [function = std::move(function)](std::vector const & column_indices) -> detail::table_callback { return [function, column_indices](container & container, detail::table & table, std::uint32_t row, util::hash_set const & detached_components, bool force){ bool const invoke = force || (detail::contains_helper::contains(detached_components) || ...); @@ -690,9 +694,11 @@ namespace psemek::ecs }; }; - query_cache cache = this->cache_impl(nullptr, std::move(destructor)); + query_cache cache = this->cache(); - callback_caches_.push_back(cache); + cache->destructor_factories.push_back(destructor_factory); + for (auto const & entry : cache->entries) + entry.table->add_destructor(destructor_factory(entry.columns_indices)); } template @@ -701,24 +707,6 @@ namespace psemek::ecs return index_container_.get(*this, std::forward(args)...); } - template - query_cache container::cache_impl(Constructor && constructor, Destructor && destructor) - { - typename detail::filter_with ...>>::type with_uuids; - typename detail::filter_without...>>::type without_uuids; - - auto result = query_cache_container_.create(with_uuids.get(), without_uuids.get()); - - result->constructor_factory = std::move(constructor); - result->destructor_factory = std::move(destructor); - - table_container_.apply([&](detail::table & table){ - result->add(&table); - }, with_uuids.get(), without_uuids.get()); - - return result; - } - template std::size_t container::memory_usage() { diff --git a/libs/ecs/include/psemek/ecs/detail/query_cache.hpp b/libs/ecs/include/psemek/ecs/detail/query_cache.hpp index e4316f3f..2875801c 100644 --- a/libs/ecs/include/psemek/ecs/detail/query_cache.hpp +++ b/libs/ecs/include/psemek/ecs/detail/query_cache.hpp @@ -24,8 +24,10 @@ namespace psemek::ecs::detail std::vector without_uuids; std::list entries; - util::function const &)> constructor_factory; - util::function const &)> destructor_factory; + using callback_factory = util::function const &)>; + + std::vector constructor_factories; + std::vector destructor_factories; void add(table * table); }; diff --git a/libs/ecs/include/psemek/ecs/detail/query_cache_container.hpp b/libs/ecs/include/psemek/ecs/detail/query_cache_container.hpp index af9c232d..c784767a 100644 --- a/libs/ecs/include/psemek/ecs/detail/query_cache_container.hpp +++ b/libs/ecs/include/psemek/ecs/detail/query_cache_container.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -9,12 +10,12 @@ namespace psemek::ecs::detail { - struct query_cache_set + struct query_cache_data { std::size_t hash; util::hash_set with_uuids; util::hash_set without_uuids; - std::vector> caches; + std::shared_ptr cache; }; struct query_cache_hash @@ -24,7 +25,7 @@ namespace psemek::ecs::detail return component_hash(uuids.first, uuids.second); } - std::size_t operator()(std::unique_ptr const & set) const + std::size_t operator()(std::unique_ptr const & set) const { return set->hash; } @@ -32,7 +33,7 @@ namespace psemek::ecs::detail struct query_cache_equal { - bool operator()(std::pair, util::span> const & uuids, std::unique_ptr const & set) const + bool operator()(std::pair, util::span> const & uuids, std::unique_ptr const & set) const { if (uuids.first.size() != set->with_uuids.size()) return false; @@ -51,7 +52,7 @@ namespace psemek::ecs::detail return true; } - bool operator()(std::unique_ptr const & set1, std::unique_ptr const & set2) const + bool operator()(std::unique_ptr const & set1, std::unique_ptr const & set2) const { return set1.get() == set2.get(); } @@ -60,41 +61,49 @@ namespace psemek::ecs::detail // TODO: store query caches in a bitmask trie balanced by subtree size struct query_cache_container { - std::shared_ptr create(util::span with_uuids, util::span without_uuids) + std::shared_ptr create(util::span with_uuids, util::span without_uuids, table_container & table_container) { - auto result = std::make_shared(); - result->with_uuids.assign(with_uuids.begin(), with_uuids.end()); - result->without_uuids.assign(without_uuids.begin(), without_uuids.end()); auto it = caches_.find(std::pair{with_uuids, without_uuids}); if (it == caches_.end()) { - auto value = std::make_unique(); + auto value = std::make_unique(); + value->cache = std::make_shared(); for (auto const & uuid : with_uuids) + { value->with_uuids.insert(uuid); + value->cache->with_uuids.push_back(uuid); + } for (auto const & uuid : without_uuids) + { value->without_uuids.insert(uuid); + value->cache->without_uuids.push_back(uuid); + } value->hash = component_hash(with_uuids, without_uuids); + + table_container.apply([&](table & table){ + value->cache->add(&table); + }, with_uuids, without_uuids); + it = caches_.insert(std::move(value)).first; } - it->get()->caches.push_back(result); - return result; + return it->get()->cache; } template void apply(Function && function, ContainsUUID && contains_uuid) { - for (auto & cache_set : caches_) + for (auto & cache_data : caches_) { bool good = true; - for (auto const & uuid : cache_set->with_uuids) + for (auto const & uuid : cache_data->with_uuids) if (!contains_uuid(uuid)) { good = false; break; } - for (auto const & uuid : cache_set->without_uuids) + for (auto const & uuid : cache_data->without_uuids) if (contains_uuid(uuid)) { good = false; @@ -104,19 +113,17 @@ namespace psemek::ecs::detail if (!good) continue; - filter(cache_set->caches); - for (auto & cache : cache_set->caches) - function(*cache.lock()); + function(*(cache_data->cache)); } } - private: - util::hash_set, query_cache_hash, query_cache_equal> caches_; - - static void filter(std::vector> & caches) + std::size_t cache_count() const { - caches.erase(std::remove_if(caches.begin(), caches.end(), [](std::weak_ptr const & weak){ return !weak.lock(); }), caches.end()); + return caches_.size(); } + + private: + util::hash_set, query_cache_hash, query_cache_equal> caches_; }; } diff --git a/libs/ecs/include/psemek/ecs/detail/table_container.hpp b/libs/ecs/include/psemek/ecs/detail/table_container.hpp index 1d9474b4..f10ae2e0 100644 --- a/libs/ecs/include/psemek/ecs/detail/table_container.hpp +++ b/libs/ecs/include/psemek/ecs/detail/table_container.hpp @@ -4,8 +4,6 @@ #include #include -#include - namespace psemek::ecs::detail { @@ -49,6 +47,8 @@ namespace psemek::ecs::detail template void apply(Function && function, util::span with_uuids, util::span without_uuids); + std::size_t table_count() const; + private: util::hash_set, table_hashset_hash, table_hashset_equal> tables_; }; @@ -93,4 +93,9 @@ namespace psemek::ecs::detail } } + inline std::size_t table_container::table_count() const + { + return tables_.size(); + } + } diff --git a/libs/ecs/source/container.cpp b/libs/ecs/source/container.cpp index c97953de..96fd665a 100644 --- a/libs/ecs/source/container.cpp +++ b/libs/ecs/source/container.cpp @@ -116,4 +116,14 @@ namespace psemek::ecs } } + std::size_t container::cache_count() + { + return query_cache_container_.cache_count(); + } + + std::size_t container::table_count() + { + return table_container_.table_count(); + } + } diff --git a/libs/ecs/source/detail/query_cache.cpp b/libs/ecs/source/detail/query_cache.cpp index 6c717937..1df6a577 100644 --- a/libs/ecs/source/detail/query_cache.cpp +++ b/libs/ecs/source/detail/query_cache.cpp @@ -12,11 +12,11 @@ namespace psemek::ecs::detail for (auto const & uuid : with_uuids) entry.columns_indices.push_back(*table->column_index(uuid)); - if (constructor_factory) - table->add_constructor(constructor_factory(entry.columns_indices)); + for (auto const & factory : constructor_factories) + entry.table->add_constructor(factory(entry.columns_indices)); - if (destructor_factory) - table->add_destructor(destructor_factory(entry.columns_indices)); + for (auto const & factory : destructor_factories) + entry.table->add_destructor(factory(entry.columns_indices)); } }