Parcourir la source

registry:
* prevent groups from being used with tombstones
* support to compact one, several or all pools

Michele Caini il y a 4 ans
Parent
commit
03511f39b1
2 fichiers modifiés avec 239 ajouts et 74 suppressions
  1. 24 5
      src/entt/entity/registry.hpp
  2. 215 69
      test/entt/entity/registry.cpp

+ 24 - 5
src/entt/entity/registry.hpp

@@ -16,6 +16,7 @@
 #include "../core/fwd.hpp"
 #include "../core/type_info.hpp"
 #include "../core/type_traits.hpp"
+#include "component.hpp"
 #include "entity.hpp"
 #include "fwd.hpp"
 #include "group.hpp"
@@ -58,6 +59,7 @@ class basic_registry {
 
     template<typename... Exclude, typename... Get, typename... Owned>
     struct group_handler<exclude_t<Exclude...>, get_t<Get...>, Owned...> {
+        static_assert(!std::disjunction_v<typename component_traits<Owned>::in_place_delete...>, "Groups do not support in-place delete");
         static_assert(std::conjunction_v<std::is_same<Owned, std::remove_const_t<Owned>>..., std::is_same<Get, std::remove_const_t<Get>>..., std::is_same<Exclude, std::remove_const_t<Exclude>>...>, "One or more component types are invalid");
         std::conditional_t<sizeof...(Owned) == 0, basic_sparse_set<Entity>, std::size_t> current{};
 
@@ -133,15 +135,14 @@ class basic_registry {
     auto recycle_identifier() ENTT_NOEXCEPT {
         ENTT_ASSERT(free_list != null, "No entities available");
         const auto curr = traits_type::to_entity(free_list);
-        const auto version = traits_type::to_version(entities[curr]);
-        free_list = entities[curr];
-        return entities[curr] = traits_type::to_type(curr, version);
+        free_list = traits_type::to_type(entities[curr], tombstone);
+        return (entities[curr] = traits_type::to_type(curr, traits_type::to_version(entities[curr])));
     }
 
     auto release_entity(const Entity entity, const typename traits_type::version_type version) {
         const auto entt = traits_type::to_entity(entity);
         entities[entt] = traits_type::to_type(traits_type::to_integral(free_list), version + (traits_type::to_type(null, version) == tombstone));
-        free_list = traits_type::to_type(entt, {});
+        free_list = traits_type::to_type(entt, traits_type::to_version(tombstone));
         return traits_type::to_version(entities[entt]);
     }
 
@@ -744,6 +745,24 @@ public:
         }
     }
 
+    /**
+     * @brief Removes all tombstones from a registry or only the pools for the
+     * given components.
+     * @tparam Component Types of components for which to clear all tombstones.
+     */
+    template<typename... Component>
+    void compact() {
+        if constexpr(sizeof...(Component) == 0) {
+            for(auto pos = pools.size(); pos; --pos) {
+                if(auto &pdata = pools[pos-1]; pdata.pool) {
+                    pdata.pool->compact();
+                }
+            }
+        } else {
+            (assure<Component>()->compact(), ...);
+        }
+    }
+
     /*! @copydoc remove */
     template<typename... Component>
     [[deprecated("Use ::remove instead")]]
@@ -1619,7 +1638,7 @@ private:
     mutable std::vector<pool_data> pools{};
     std::vector<group_data> groups{};
     std::vector<entity_type> entities{};
-    entity_type free_list{null};
+    entity_type free_list{tombstone};
 };
 
 

+ 215 - 69
test/entt/entity/registry.cpp

@@ -10,6 +10,13 @@
 #include <entt/entity/entity.hpp>
 
 struct empty_type {};
+struct stable_type { int value; };
+
+template<>
+struct entt::component_traits<stable_type> {
+    using in_place_delete = std::true_type;
+    using ignore_if_empty = std::true_type;
+};
 
 struct non_default_constructible {
     non_default_constructible(int v): value{v} {}
@@ -524,6 +531,93 @@ TEST(Registry, CreateDestroyCornerCase) {
     ASSERT_EQ(registry.current(e1), entt::registry::version_type{1});
 }
 
+TEST(Registry, RangeDestroy) {
+    entt::registry registry;
+    const auto iview = registry.view<int>();
+    const auto icview = registry.view<int, char>();
+
+    const auto e0 = registry.create();
+    const auto e1 = registry.create();
+    const auto e2 = registry.create();
+
+    registry.emplace<int>(e0);
+    registry.emplace<char>(e0);
+    registry.emplace<double>(e0);
+
+    registry.emplace<int>(e1);
+    registry.emplace<char>(e1);
+
+    registry.emplace<int>(e2);
+
+    ASSERT_TRUE(registry.valid(e0));
+    ASSERT_TRUE(registry.valid(e1));
+    ASSERT_TRUE(registry.valid(e2));
+
+    registry.destroy(icview.begin(), icview.end());
+
+    ASSERT_FALSE(registry.valid(e0));
+    ASSERT_FALSE(registry.valid(e1));
+    ASSERT_TRUE(registry.valid(e2));
+
+    ASSERT_EQ(registry.size<int>(), 1u);
+    ASSERT_EQ(registry.size<char>(), 0u);
+    ASSERT_EQ(registry.size<double>(), 0u);
+
+    registry.destroy(iview.begin(), iview.end());
+
+    ASSERT_FALSE(registry.valid(e2));
+    ASSERT_NO_FATAL_FAILURE(registry.destroy(iview.begin(), iview.end()));
+    ASSERT_EQ(iview.size(), 0u);
+    ASSERT_EQ(icview.size_hint(), 0u);
+
+    ASSERT_EQ(registry.size<int>(), 0u);
+    ASSERT_EQ(registry.size<char>(), 0u);
+    ASSERT_EQ(registry.size<double>(), 0u);
+}
+
+TEST(Registry, StableDestroy) {
+    entt::registry registry;
+    const auto iview = registry.view<int>();
+    const auto icview = registry.view<int, stable_type>();
+
+    const auto e0 = registry.create();
+    const auto e1 = registry.create();
+    const auto e2 = registry.create();
+
+    registry.emplace<int>(e0);
+    registry.emplace<stable_type>(e0);
+    registry.emplace<double>(e0);
+
+    registry.emplace<int>(e1);
+    registry.emplace<stable_type>(e1);
+
+    registry.emplace<int>(e2);
+
+    ASSERT_TRUE(registry.valid(e0));
+    ASSERT_TRUE(registry.valid(e1));
+    ASSERT_TRUE(registry.valid(e2));
+
+    registry.destroy(icview.begin(), icview.end());
+
+    ASSERT_FALSE(registry.valid(e0));
+    ASSERT_FALSE(registry.valid(e1));
+    ASSERT_TRUE(registry.valid(e2));
+
+    ASSERT_EQ(registry.size<int>(), 1u);
+    ASSERT_EQ(registry.size<stable_type>(), 2u);
+    ASSERT_EQ(registry.size<double>(), 0u);
+
+    registry.destroy(iview.begin(), iview.end());
+
+    ASSERT_FALSE(registry.valid(e2));
+    ASSERT_EQ(iview.size(), 0u);
+    ASSERT_EQ(icview.size_hint(), 0u);
+
+    ASSERT_EQ(registry.size<int>(), 0u);
+    ASSERT_EQ(registry.size<stable_type>(), 2u);
+    ASSERT_EQ(registry.size<double>(), 0u);
+}
+
 TEST(Registry, VersionOverflow) {
     using traits_type = entt::entt_traits<entt::entity>;
 
@@ -1221,40 +1315,6 @@ TEST(Registry, Signals) {
     ASSERT_EQ(listener.last, e0);
 }
 
-TEST(Registry, RangeDestroy) {
-    entt::registry registry;
-    const auto iview = registry.view<int>();
-    const auto icview = registry.view<int, char>();
-
-    const auto e0 = registry.create();
-    const auto e1 = registry.create();
-    const auto e2 = registry.create();
-
-    registry.emplace<int>(e0);
-    registry.emplace<char>(e0);
-    registry.emplace<double>(e0);
-
-    registry.emplace<int>(e1);
-    registry.emplace<char>(e1);
-
-    registry.emplace<int>(e2);
-
-    ASSERT_TRUE(registry.valid(e0));
-    ASSERT_TRUE(registry.valid(e1));
-    ASSERT_TRUE(registry.valid(e2));
-
-    registry.destroy(icview.begin(), icview.end());
-
-    ASSERT_FALSE(registry.valid(e0));
-    ASSERT_FALSE(registry.valid(e1));
-    ASSERT_TRUE(registry.valid(e2));
-
-    registry.destroy(iview.begin(), iview.end());
-
-    ASSERT_FALSE(registry.valid(e2));
-    ASSERT_NO_FATAL_FAILURE(registry.destroy(iview.begin(), iview.end()));
-}
-
 TEST(Registry, Insert) {
     entt::registry registry;
 
@@ -1295,6 +1355,8 @@ TEST(Registry, Insert) {
 
 TEST(Registry, Erase) {
     entt::registry registry;
+    const auto iview = registry.view<int>();
+    const auto icview = registry.view<int, char>();
 
     const auto e0 = registry.create();
     const auto e1 = registry.create();
@@ -1309,60 +1371,85 @@ TEST(Registry, Erase) {
 
     registry.emplace<int>(e2);
 
-    ASSERT_TRUE(registry.all_of<int>(e0));
+    ASSERT_TRUE(registry.any_of<int>(e0));
     ASSERT_TRUE(registry.all_of<int>(e1));
-    ASSERT_TRUE(registry.all_of<int>(e2));
+    ASSERT_TRUE(registry.any_of<int>(e2));
 
-    const auto view = registry.view<int, char>();
+    registry.erase<int, char>(e0);
+    registry.erase<int, char>(icview.begin(), icview.end());
+    registry.erase<int, char>(icview.begin(), icview.end());
 
-    registry.erase<int>(e0);
-    registry.erase<int>(view.begin(), view.end());
-    registry.erase<int>(view.begin(), view.end());
+    ASSERT_FALSE(registry.any_of<int>(e0));
+    ASSERT_FALSE(registry.all_of<int>(e1));
+    ASSERT_TRUE(registry.any_of<int>(e2));
+
+    ASSERT_EQ(registry.size<int>(), 1u);
+    ASSERT_EQ(registry.size<char>(), 0u);
+    ASSERT_EQ(registry.size<double>(), 1u);
+
+    registry.erase<int>(iview.begin(), iview.end());
 
     ASSERT_DEATH(registry.erase<int>(e0), "");
     ASSERT_DEATH(registry.erase<int>(e1), "");
 
-    ASSERT_FALSE(registry.all_of<int>(e0));
-    ASSERT_FALSE(registry.all_of<int>(e1));
-    ASSERT_TRUE(registry.all_of<int>(e2));
+    ASSERT_FALSE(registry.any_of<int>(e2));
+    ASSERT_NO_FATAL_FAILURE(registry.erase<int>(iview.begin(), iview.end()));
+
+    ASSERT_EQ(registry.size<int>(), 0u);
+    ASSERT_EQ(registry.size<char>(), 0u);
+    ASSERT_EQ(registry.size<double>(), 1u);
 }
 
-TEST(Registry, RangeErase) {
+TEST(Registry, StableErase) {
     entt::registry registry;
     const auto iview = registry.view<int>();
-    const auto icview = registry.view<int, char>();
+    const auto icview = registry.view<int, stable_type>();
 
     const auto e0 = registry.create();
     const auto e1 = registry.create();
     const auto e2 = registry.create();
 
     registry.emplace<int>(e0);
-    registry.emplace<char>(e0);
+    registry.emplace<stable_type>(e0);
     registry.emplace<double>(e0);
 
     registry.emplace<int>(e1);
-    registry.emplace<char>(e1);
+    registry.emplace<stable_type>(e1);
 
     registry.emplace<int>(e2);
 
     ASSERT_TRUE(registry.any_of<int>(e0));
-    ASSERT_TRUE(registry.any_of<int>(e1));
+    ASSERT_TRUE(registry.all_of<int>(e1));
     ASSERT_TRUE(registry.any_of<int>(e2));
 
-    registry.erase<int, char>(icview.begin(), icview.end());
+    registry.erase<int, stable_type>(e0);
+    registry.erase<int, stable_type>(icview.begin(), icview.end());
+    registry.erase<int, stable_type>(icview.begin(), icview.end());
 
     ASSERT_FALSE(registry.any_of<int>(e0));
-    ASSERT_FALSE(registry.any_of<int>(e1));
+    ASSERT_FALSE(registry.all_of<int>(e1));
     ASSERT_TRUE(registry.any_of<int>(e2));
 
+    ASSERT_EQ(registry.size<int>(), 1u);
+    ASSERT_EQ(registry.size<stable_type>(), 2u);
+    ASSERT_EQ(registry.size<double>(), 1u);
+
     registry.erase<int>(iview.begin(), iview.end());
 
+    ASSERT_DEATH(registry.erase<int>(e0), "");
+    ASSERT_DEATH(registry.erase<int>(e1), "");
+
     ASSERT_FALSE(registry.any_of<int>(e2));
-    ASSERT_NO_FATAL_FAILURE(registry.erase<int>(iview.begin(), iview.end()));
+
+    ASSERT_EQ(registry.size<int>(), 0u);
+    ASSERT_EQ(registry.size<stable_type>(), 2u);
+    ASSERT_EQ(registry.size<double>(), 1u);
 }
 
 TEST(Registry, Remove) {
     entt::registry registry;
+    const auto iview = registry.view<int>();
+    const auto icview = registry.view<int, char>();
 
     const auto e0 = registry.create();
     const auto e1 = registry.create();
@@ -1377,54 +1464,113 @@ TEST(Registry, Remove) {
 
     registry.emplace<int>(e2);
 
-    ASSERT_TRUE(registry.all_of<int>(e0));
+    ASSERT_TRUE(registry.any_of<int>(e0));
     ASSERT_TRUE(registry.all_of<int>(e1));
-    ASSERT_TRUE(registry.all_of<int>(e2));
+    ASSERT_TRUE(registry.any_of<int>(e2));
 
-    const auto view = registry.view<int, char>();
+    registry.remove<int, char>(e0);
 
-    ASSERT_EQ(registry.remove<int>(e0), 1u);
-    ASSERT_EQ(registry.remove<int>(view.begin(), view.end()), 1u);
-    ASSERT_EQ(registry.remove<int>(view.begin(), view.end()), 0u);
-    ASSERT_EQ(registry.remove<int>(e1), 0u);
+    ASSERT_EQ((registry.remove<int, char>(icview.begin(), icview.end())), 2u);
+    ASSERT_EQ((registry.remove<int, char>(icview.begin(), icview.end())), 0u);
 
-    ASSERT_FALSE(registry.all_of<int>(e0));
+    ASSERT_FALSE(registry.any_of<int>(e0));
     ASSERT_FALSE(registry.all_of<int>(e1));
-    ASSERT_TRUE(registry.all_of<int>(e2));
+    ASSERT_TRUE(registry.any_of<int>(e2));
+
+    ASSERT_EQ(registry.size<int>(), 1u);
+    ASSERT_EQ(registry.size<char>(), 0u);
+    ASSERT_EQ(registry.size<double>(), 1u);
+
+    ASSERT_EQ((registry.remove<int>(iview.begin(), iview.end())), 1u);
+
+    ASSERT_EQ(registry.remove<int>(e0), 0u);
+    ASSERT_EQ(registry.remove<int>(e1), 0u);
+
+    ASSERT_FALSE(registry.any_of<int>(e2));
+    ASSERT_EQ(registry.remove<int>(iview.begin(), iview.end()), 0u);
+
+    ASSERT_EQ(registry.size<int>(), 0u);
+    ASSERT_EQ(registry.size<char>(), 0u);
+    ASSERT_EQ(registry.size<double>(), 1u);
 }
 
-TEST(Registry, RangeRemove) {
+TEST(Registry, StableRemove) {
     entt::registry registry;
     const auto iview = registry.view<int>();
-    const auto icview = registry.view<int, char>();
+    const auto icview = registry.view<int, stable_type>();
 
     const auto e0 = registry.create();
     const auto e1 = registry.create();
     const auto e2 = registry.create();
 
     registry.emplace<int>(e0);
-    registry.emplace<char>(e0);
+    registry.emplace<stable_type>(e0);
     registry.emplace<double>(e0);
 
     registry.emplace<int>(e1);
-    registry.emplace<char>(e1);
+    registry.emplace<stable_type>(e1);
 
     registry.emplace<int>(e2);
 
     ASSERT_TRUE(registry.any_of<int>(e0));
-    ASSERT_TRUE(registry.any_of<int>(e1));
+    ASSERT_TRUE(registry.all_of<int>(e1));
     ASSERT_TRUE(registry.any_of<int>(e2));
 
-    registry.remove<int, char>(icview.begin(), icview.end());
+    registry.remove<int, stable_type>(e0);
+
+    ASSERT_EQ((registry.remove<int, stable_type>(icview.begin(), icview.end())), 2u);
+    ASSERT_EQ((registry.remove<int, stable_type>(icview.begin(), icview.end())), 0u);
 
     ASSERT_FALSE(registry.any_of<int>(e0));
-    ASSERT_FALSE(registry.any_of<int>(e1));
+    ASSERT_FALSE(registry.all_of<int>(e1));
     ASSERT_TRUE(registry.any_of<int>(e2));
 
-    registry.remove<int>(iview.begin(), iview.end());
+    ASSERT_EQ(registry.size<int>(), 1u);
+    ASSERT_EQ(registry.size<stable_type>(), 2u);
+    ASSERT_EQ(registry.size<double>(), 1u);
+
+    ASSERT_EQ((registry.remove<int>(iview.begin(), iview.end())), 1u);
+
+    ASSERT_EQ(registry.remove<int>(e0), 0u);
+    ASSERT_EQ(registry.remove<int>(e1), 0u);
 
     ASSERT_FALSE(registry.any_of<int>(e2));
-    ASSERT_EQ(registry.remove<int>(iview.begin(), iview.end()), 0u);
+
+    ASSERT_EQ(registry.size<int>(), 0u);
+    ASSERT_EQ(registry.size<stable_type>(), 2u);
+    ASSERT_EQ(registry.size<double>(), 1u);
+}
+
+TEST(Registry, Compact) {
+    entt::registry registry;
+
+    const auto e0 = registry.create();
+    const auto e1 = registry.create();
+
+    registry.emplace<int>(e0);
+    registry.emplace<stable_type>(e0);
+
+    registry.emplace<int>(e1);
+    registry.emplace<stable_type>(e1);
+
+    ASSERT_EQ(registry.size<int>(), 2u);
+    ASSERT_EQ(registry.size<stable_type>(), 2u);
+
+    registry.destroy(e0);
+    registry.destroy(e1);
+
+    ASSERT_EQ(registry.size<int>(), 0u);
+    ASSERT_EQ(registry.size<stable_type>(), 2u);
+
+    registry.compact<int>();
+
+    ASSERT_EQ(registry.size<int>(), 0u);
+    ASSERT_EQ(registry.size<stable_type>(), 2u);
+
+    registry.compact();
+
+    ASSERT_EQ(registry.size<int>(), 0u);
+    ASSERT_EQ(registry.size<stable_type>(), 0u);
 }
 
 TEST(Registry, NonOwningGroupInterleaved) {