Просмотр исходного кода

registry: added ::destroyed, made ::assign much faster (breaking changes, close #522)

Michele Caini 5 лет назад
Родитель
Сommit
2d4b00ec24
3 измененных файлов с 50 добавлено и 33 удалено
  1. 31 22
      src/entt/entity/registry.hpp
  2. 17 10
      src/entt/entity/snapshot.hpp
  3. 2 1
      test/entt/entity/registry.cpp

+ 31 - 22
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`.<br/>
+     * 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<typename It>
     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`.<br/>
-     * 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`.<br/>
+     * 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<typename It>
-    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<typename traits_type::entity_type>(pos)}, version(entities[pos]));
-            }
-        }
+        available = destroyed;
     }
 
     /**
@@ -963,7 +972,7 @@ public:
      */
     template<typename Func>
     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<pool_data> pools{};
     std::vector<entity_type> entities{};
     std::vector<variable_data> vars{};
-    entity_type destroyed{null};
+    entity_type available{null};
 };
 
 

+ 17 - 10
src/entt/entity/snapshot.hpp

@@ -95,15 +95,15 @@ public:
     template<typename Archive>
     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<Entity>;
 
     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;
     }
 

+ 2 - 1
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]));