From 2d4b00ec246ab67cfc0d4bcc30d369bd054acb7a Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Tue, 27 Oct 2020 23:01:28 +0100 Subject: [PATCH] registry: added ::destroyed, made ::assign much faster (breaking changes, close #522) --- src/entt/entity/registry.hpp | 53 ++++++++++++++++++++--------------- src/entt/entity/snapshot.hpp | 27 +++++++++++------- test/entt/entity/registry.cpp | 3 +- 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index 760b1b2af..88680b100 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -134,17 +134,17 @@ class basic_registry { } Entity recycle_identifier() { - ENTT_ASSERT(destroyed != null); - const auto curr = to_integral(destroyed); + ENTT_ASSERT(available != null); + const auto curr = to_integral(available); const auto version = to_integral(entities[curr]) & (traits_type::version_mask << traits_type::entity_shift); - destroyed = entity_type{to_integral(entities[curr]) & traits_type::entity_mask}; + available = entity_type{to_integral(entities[curr]) & traits_type::entity_mask}; return entities[curr] = entity_type{curr | version}; } void release_entity(const Entity entity, const typename traits_type::version_type version) { const auto entt = to_integral(entity) & traits_type::entity_mask; - entities[entt] = entity_type{to_integral(destroyed) | (typename traits_type::entity_type{version} << traits_type::entity_shift)}; - destroyed = entity_type{entt}; + entities[entt] = entity_type{to_integral(available) | (typename traits_type::entity_type{version} << traits_type::entity_shift)}; + available = entity_type{entt}; } public: @@ -198,7 +198,7 @@ public: */ [[nodiscard]] size_type alive() const { auto sz = entities.size(); - auto curr = destroyed; + auto curr = available; for(; curr != null; --sz) { curr = entities[to_integral(curr) & traits_type::entity_mask]; @@ -340,6 +340,18 @@ public: return entities.data(); } + /** + * @brief Returns the head of the list of destroyed entities. + * + * This function is intended for use in conjunction with `assign`.
+ * The returned entity has an invalid identifier in all cases. + * + * @return The head of the list of destroyed entities. + */ + [[nodiscard]] entity_type destroyed() const ENTT_NOEXCEPT { + return available; + } + /** * @brief Checks if an entity identifier refers to a valid entity. * @param entity An entity identifier, either valid or not. @@ -398,7 +410,7 @@ public: * @return A valid entity identifier. */ entity_type create() { - return destroyed == null ? generate_identifier() : recycle_identifier(); + return available == null ? generate_identifier() : recycle_identifier(); } /** @@ -427,7 +439,7 @@ public: } else if(const auto curr = (to_integral(entities[req]) & traits_type::entity_mask); req == curr) { entt = create(); } else { - auto *it = &destroyed; + auto *it = &available; for(; (to_integral(*it) & traits_type::entity_mask) != req; it = &entities[to_integral(*it) & traits_type::entity_mask]); *it = entity_type{curr | (to_integral(*it) & (traits_type::version_mask << traits_type::entity_shift))}; entt = entities[req] = hint; @@ -447,7 +459,7 @@ public: */ template void create(It first, It last) { - for(; destroyed != null && first != last; ++first) { + for(; available != null && first != last; ++first) { *first = recycle_identifier(); } @@ -459,9 +471,11 @@ public: /** * @brief Assigns entities to an empty registry. * - * This function is intended for use in conjunction with `raw`.
- * Don't try to inject ranges of randomly generated entities. There is no - * guarantee that a registry will continue to work properly in this case. + * This function is intended for use in conjunction with `raw` and + * `assign`.
+ * Don't try to inject ranges of randomly generated entities nor the _wrong_ + * head for the list of destroyed entities. There is no guarantee that a + * registry will continue to work properly in this case. * * @warning * An assertion will abort the execution at runtime in debug mode if all @@ -470,18 +484,13 @@ public: * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. + * @param destroyed The head of the list of destroyed entities. */ template - void assign(It first, It last) { + void assign(It first, It last, const entity_type destroyed) { ENTT_ASSERT(std::all_of(pools.cbegin(), pools.cend(), [](auto &&pdata) { return !pdata.pool || pdata.pool->empty(); })); entities.assign(first, last); - destroyed = null; - - for(std::size_t pos{}, end = entities.size(); pos < end; ++pos) { - if((to_integral(entities[pos]) & traits_type::entity_mask) != pos) { - release_entity(entity_type{static_cast(pos)}, version(entities[pos])); - } - } + available = destroyed; } /** @@ -963,7 +972,7 @@ public: */ template void each(Func func) const { - if(destroyed == null) { + if(available == null) { for(auto pos = entities.size(); pos; --pos) { func(entities[pos-1]); } @@ -1633,7 +1642,7 @@ private: mutable std::vector pools{}; std::vector entities{}; std::vector vars{}; - entity_type destroyed{null}; + entity_type available{null}; }; diff --git a/src/entt/entity/snapshot.hpp b/src/entt/entity/snapshot.hpp index d34e87c6f..b2c243846 100644 --- a/src/entt/entity/snapshot.hpp +++ b/src/entt/entity/snapshot.hpp @@ -95,15 +95,15 @@ public: template const basic_snapshot & entities(Archive &archive) const { const auto sz = reg->size(); - auto first = reg->data(); - const auto last = first + sz; archive(typename traits_type::entity_type(sz)); - while(first != last) { - archive(*(first++)); + for(auto first = reg->data(), last = first + sz; first != last; ++first) { + archive(*first); } + archive(reg->destroyed()); + return *this; } @@ -234,7 +234,10 @@ public: archive(all[pos]); } - reg->assign(all.cbegin(), all.cend()); + entity_type destroyed; + archive(destroyed); + + reg->assign(all.cbegin(), all.cend(), destroyed); return *this; } @@ -302,12 +305,10 @@ class basic_continuous_loader { using traits_type = entt_traits; void destroy(Entity entt) { - const auto it = remloc.find(entt); - - if(it == remloc.cend()) { + if(const auto it = remloc.find(entt); it == remloc.cend()) { const auto local = reg->create(); remloc.emplace(entt, std::make_pair(local, true)); - reg->destroy(local); + reg->destroy(local, 0u); } } @@ -318,7 +319,10 @@ class basic_continuous_loader { const auto local = reg->create(); remloc.emplace(entt, std::make_pair(local, true)); } else { - remloc[entt].first = reg->valid(remloc[entt].first) ? remloc[entt].first : reg->create(); + if(!reg->valid(remloc[entt].first)) { + remloc[entt].first = reg->create(); + } + // set the dirty flag remloc[entt].second = true; } @@ -451,6 +455,9 @@ public: } } + // discards the head of the list of destroyed entities + archive(entt); + return *this; } diff --git a/test/entt/entity/registry.cpp b/test/entt/entity/registry.cpp index 7020a86b1..cd0ff3d81 100644 --- a/test/entt/entity/registry.cpp +++ b/test/entt/entity/registry.cpp @@ -1404,7 +1404,8 @@ TEST(Registry, AssignEntities) { registry.destroy(entities[2]); entt::registry other; - other.assign(registry.data(), registry.data() + registry.size()); + const auto *data = registry.data(); + other.assign(data, data + registry.size(), registry.destroyed()); ASSERT_EQ(registry.size(), other.size()); ASSERT_TRUE(other.valid(entities[0]));