Michele Caini il y a 8 ans
Parent
commit
29de6d89d4
3 fichiers modifiés avec 87 ajouts et 121 suppressions
  1. 22 15
      README.md
  2. 1 0
      TODO
  3. 64 106
      src/entt/entity/registry.hpp

+ 22 - 15
README.md

@@ -202,8 +202,8 @@ Dell XPS 13 out of the mid 2014):
 
 
 | Benchmark | EntityX (compile-time) | EnTT |
 | Benchmark | EntityX (compile-time) | EnTT |
 |-----------|-------------|-------------|
 |-----------|-------------|-------------|
-| Create 1M entities | 0.0167s | **0.0046s** |
-| Destroy 1M entities | 0.0053s | **0.0037s** |
+| Create 1M entities | 0.0147s | **0.0046s** |
+| Destroy 1M entities | 0.0053s | **0.0048s** |
 | Standard view, 1M entities, one component | 0.0012s | **1.9e-07s** |
 | Standard view, 1M entities, one component | 0.0012s | **1.9e-07s** |
 | Standard view, 1M entities, two components | 0.0012s | **3.8e-07s** |
 | Standard view, 1M entities, two components | 0.0012s | **3.8e-07s** |
 | Standard view, 1M entities, two components<br/>Half of the entities have all the components | 0.0009s | **3.8e-07s** |
 | Standard view, 1M entities, two components<br/>Half of the entities have all the components | 0.0009s | **3.8e-07s** |
@@ -461,8 +461,8 @@ velocity.dx = 0.;
 velocity.dy = 0.;
 velocity.dy = 0.;
 ```
 ```
 
 
-Note that `accommodate` is a slightly faster alternative for the following
-`if`/`else` statement and nothing more:
+Note that `accommodate` is mostly syntactic sugar for the following `if`/`else`
+statement and nothing more:
 
 
 ```cpp
 ```cpp
 if(registry.has<Comp>(entity)) {
 if(registry.has<Comp>(entity)) {
@@ -570,6 +570,13 @@ notification and the entity affected by the change. Note also that:
 * Listeners are invoked **before** components have been removed from entities.
 * Listeners are invoked **before** components have been removed from entities.
 * The order of invocation of the listeners isn't guaranteed in any case.
 * The order of invocation of the listeners isn't guaranteed in any case.
 
 
+There are also some limitations on what a listener can and cannot do. In
+particular, connecting and disconnecting other functions from within the body of
+a listener should be avoided. It can result in undefined behavior.<br/>
+In general, events and therefore listeners must not be used as replacements for
+systems. They should not contain much logic and interactions with a registry
+should be kept to a minimum, if possible.
+
 ### Single instance components
 ### Single instance components
 
 
 In those cases where all what is needed is a single instance component, tags are
 In those cases where all what is needed is a single instance component, tags are
@@ -757,8 +764,8 @@ It isn't necessary to invoke all these functions each and every time. What
 functions to use in which case mostly depends on the goal and there is not a
 functions to use in which case mostly depends on the goal and there is not a
 golden rule to do that.
 golden rule to do that.
 
 
-The `entities` member function asks to the registry to serialize all the
-entities that are still in use along with their versions. On the other side, the
+The `entities` member function asks the registry to serialize all the entities
+that are still in use along with their versions. On the other side, the
 `destroyed` member function tells to the registry to serialize the entities that
 `destroyed` member function tells to the registry to serialize the entities that
 have been destroyed and are no longer in use.<br/>
 have been destroyed and are no longer in use.<br/>
 These two functions can be used to save and restore the whole set of entities
 These two functions can be used to save and restore the whole set of entities
@@ -802,10 +809,10 @@ Example of use:
 InputArchive input;
 InputArchive input;
 
 
 registry.restore()
 registry.restore()
-    .entities()
-    .destroyed()
-    .component<AComponent, AnotherComponent>(output)
-    .tag<MyTag>(output)
+    .entities(input)
+    .destroyed(input)
+    .component<AComponent, AnotherComponent>(input)
+    .tag<MyTag>(input)
     .orphans();
     .orphans();
 ```
 ```
 
 
@@ -1341,16 +1348,16 @@ not be used frequently to avoid the risk of a performance hit.
   construction of the pools for all their components and access them directly,
   construction of the pools for all their components and access them directly,
   thus avoiding all the checks.
   thus avoiding all the checks.
 
 
-* Most of the _ECS_ available out there have an annoying limitation (at least
+* Most of the _ECS_ available out there have some annoying limitations (at least
   from my point of view): entities and components cannot be created and/or
   from my point of view): entities and components cannot be created and/or
   destroyed during iterations.<br/>
   destroyed during iterations.<br/>
   `EnTT` partially solves the problem with a few limitations:
   `EnTT` partially solves the problem with a few limitations:
 
 
   * Creating entities and components is allowed during iterations.
   * Creating entities and components is allowed during iterations.
-  * Deleting an entity or removing its components is allowed during
-    iterations if it's the one currently returned by a view. For all the
-    other entities, destroying them or removing their components isn't
-    allowed and it can result in undefined behavior.
+  * Deleting an entity or removing its components is allowed during iterations
+    if it's the one currently returned by a view. For all the other entities,
+    destroying them or removing their components isn't allowed and it can result
+    in undefined behavior.
 
 
   Iterators are invalidated and the behavior is undefined if an entity is
   Iterators are invalidated and the behavior is undefined if an entity is
   modified or destroyed and it's not the one currently returned by the
   modified or destroyed and it's not the one currently returned by the

+ 1 - 0
TODO

@@ -5,6 +5,7 @@
 * define a macro for the noexcept policy, so as to provide users with an easy way to disable exception handling
 * define a macro for the noexcept policy, so as to provide users with an easy way to disable exception handling
 * define basic reactive systems (track entities to which component is attached, track entities from which component is removed, and so on)
 * define basic reactive systems (track entities to which component is attached, track entities from which component is removed, and so on)
 * define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc)
 * define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc)
+* does it worth it to add an optional functor to the member functions of snapshot so as to filter out instances and entities?
 * ease the assignment of tags as string (use a template class with a non-type template parameter behind the scene)
 * ease the assignment of tags as string (use a template class with a non-type template parameter behind the scene)
 * dictionary based dependency class (templates copied over) + prefabs (shared state/copy-on-write)
 * dictionary based dependency class (templates copied over) + prefabs (shared state/copy-on-write)
 * "singleton mode" for tags (see #66)
 * "singleton mode" for tags (see #66)

+ 64 - 106
src/entt/entity/registry.hpp

@@ -59,6 +59,8 @@ class Registry {
     }
     }
 
 
     struct Attachee {
     struct Attachee {
+        Attachee(Entity entity): entity{entity} {}
+        virtual ~Attachee() = default;
         Entity entity;
         Entity entity;
     };
     };
 
 
@@ -74,69 +76,20 @@ class Registry {
     };
     };
 
 
     template<typename Component>
     template<typename Component>
-    struct Pool: SparseSet<Entity, Component> {
-        using sink_type = typename SigH<void(Registry &, Entity)>::sink_type;
-
-        Pool(Registry &registry)
-            : registry{registry}
-        {}
-
-        template<typename... Args>
-        Component & construct(Entity entity, Args &&... args) {
-            auto &component = SparseSet<Entity, Component>::construct(entity, std::forward<Args>(args)...);
-            constructing.publish(registry, entity);
-            return component;
-        }
-
-        void destroy(Entity entity) override {
-            destroying.publish(registry, entity);
-            SparseSet<Entity, Component>::destroy(entity);
-        }
-
-        sink_type construction() noexcept {
-            return constructing.sink();
-        }
-
-        sink_type destruction() noexcept {
-            return destroying.sink();
-        }
-
-    private:
-        Registry &registry;
-        SigH<void(Registry &, Entity)> constructing;
-        SigH<void(Registry &, Entity)> destroying;
-    };
-
-    template<typename Component>
-    bool managed() const noexcept {
-        const auto ctype = component_family::type<Component>();
-        return ctype < pools.size() && pools[ctype];
-    }
-
-    template<typename Component>
-    const Pool<Component> & pool() const noexcept {
-        assert(managed<Component>());
-        return static_cast<Pool<Component> &>(*pools[component_family::type<Component>()]);
-    }
-
-    template<typename Component>
-    Pool<Component> & pool() noexcept {
-        return const_cast<Pool<Component> &>(const_cast<const Registry *>(this)->pool<Component>());
-    }
-
-    template<typename Component>
-    Pool<Component> & assure() {
+    SparseSet<Entity, Component> & assure() {
         const auto ctype = component_family::type<Component>();
         const auto ctype = component_family::type<Component>();
 
 
         if(!(ctype < pools.size())) {
         if(!(ctype < pools.size())) {
             pools.resize(ctype + 1);
             pools.resize(ctype + 1);
         }
         }
 
 
-        if(!pools[ctype]) {
-            pools[ctype] = std::make_unique<Pool<Component>>(*this);
+        auto &cpool = std::get<0>(pools[ctype]);
+
+        if(!cpool) {
+            cpool = std::make_unique<SparseSet<Entity, Component>>();
         }
         }
 
 
-        return pool<Component>();
+        return static_cast<SparseSet<Entity, Component> &>(*cpool);
     }
     }
 
 
 public:
 public:
@@ -150,10 +103,8 @@ public:
     using tag_type = typename tag_family::family_type;
     using tag_type = typename tag_family::family_type;
     /*! @brief Unsigned integer type. */
     /*! @brief Unsigned integer type. */
     using component_type = typename component_family::family_type;
     using component_type = typename component_family::family_type;
-
     /*! @brief Type of sink for the given component. */
     /*! @brief Type of sink for the given component. */
-    template<typename Component>
-    using sink_type = typename Pool<Component>::sink_type;
+    using sink_type = typename SigH<void(Registry &, Entity)>::sink_type;
 
 
     /*! @brief Default constructor. */
     /*! @brief Default constructor. */
     Registry() = default;
     Registry() = default;
@@ -209,7 +160,8 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     size_type size() const noexcept {
     size_type size() const noexcept {
-        return managed<Component>() ? pool<Component>().size() : size_type{};
+        const auto ctype = component_family::type<Component>();
+        return ((ctype < pools.size()) && std::get<0>(pools[ctype])) ? std::get<0>(pools[ctype])->size() : size_type{};
     }
     }
 
 
     /**
     /**
@@ -262,7 +214,8 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     bool empty() const noexcept {
     bool empty() const noexcept {
-        return managed<Component>() ? pool<Component>().empty() : true;
+        const auto ctype = component_family::type<Component>();
+        return (!(ctype < pools.size()) || !std::get<0>(pools[ctype]) || std::get<0>(pools[ctype])->empty());
     }
     }
 
 
     /**
     /**
@@ -400,11 +353,15 @@ public:
         next = entt;
         next = entt;
         ++available;
         ++available;
 
 
-        std::for_each(pools.begin(), pools.end(), [entity](auto &cpool) {
+        // changing the number of pools during a destroy is supported now
+        for(std::size_t pos = pools.size(); pos; --pos) {
+            auto &cpool = std::get<0>(pools[pos-1]);
+
             if(cpool && cpool->has(entity)) {
             if(cpool && cpool->has(entity)) {
                 cpool->destroy(entity);
                 cpool->destroy(entity);
+                std::get<2>(pools[pos-1]).publish(*this, entity);
             }
             }
-        });
+        }
     }
     }
 
 
     /**
     /**
@@ -466,7 +423,10 @@ public:
     template<typename Component, typename... Args>
     template<typename Component, typename... Args>
     Component & assign(entity_type entity, Args &&... args) {
     Component & assign(entity_type entity, Args &&... args) {
         assert(valid(entity));
         assert(valid(entity));
-        return assure<Component>().construct(entity, std::forward<Args>(args)...);
+        auto &pool = assure<Component>();
+        auto &component = pool.construct(entity, std::forward<Args>(args)...);
+        std::get<1>(pools[component_family::type<Component>()]).publish(*this, entity);
+        return component;
     }
     }
 
 
     /**
     /**
@@ -496,7 +456,8 @@ public:
     template<typename Component>
     template<typename Component>
     void remove(entity_type entity) {
     void remove(entity_type entity) {
         assert(valid(entity));
         assert(valid(entity));
-        pool<Component>().destroy(entity);
+        assure<Component>().destroy(entity);
+        std::get<2>(pools[component_family::type<Component>()]).publish(*this, entity);
     }
     }
 
 
     /**
     /**
@@ -531,8 +492,9 @@ public:
         assert(valid(entity));
         assert(valid(entity));
         using accumulator_type = bool[];
         using accumulator_type = bool[];
         bool all = true;
         bool all = true;
-        accumulator_type accumulator = { all, (all = all && managed<Component>() && pool<Component>().has(entity))... };
-        (void)accumulator;
+        auto test = [entity, this](auto ctype) { return (ctype < pools.size()) && std::get<0>(pools[ctype]) && std::get<0>(pools[ctype])->has(entity); };
+        accumulator_type accumulator = { all, (all = all && test(component_family::type<Component>()))... };
+        (void)test, (void)accumulator;
         return all;
         return all;
     }
     }
 
 
@@ -588,7 +550,9 @@ public:
     template<typename Component>
     template<typename Component>
     const Component & get(entity_type entity) const noexcept {
     const Component & get(entity_type entity) const noexcept {
         assert(valid(entity));
         assert(valid(entity));
-        return pool<Component>().get(entity);
+        const auto ctype = component_family::type<Component>();
+        assert((ctype < pools.size()) && std::get<0>(pools[ctype]));
+        return static_cast<SparseSet<Entity, Component> &>(*std::get<0>(pools[ctype])).get(entity);
     }
     }
 
 
     /**
     /**
@@ -755,9 +719,6 @@ public:
      * }
      * }
      * @endcode
      * @endcode
      *
      *
-     * Prefer this function anyway because it has slightly better
-     * performance.
-     *
      * @warning
      * @warning
      * Attempting to use an invalid entity results in undefined behavior.<br/>
      * Attempting to use an invalid entity results in undefined behavior.<br/>
      * An assertion will abort the execution at runtime in debug mode in case of
      * An assertion will abort the execution at runtime in debug mode in case of
@@ -771,12 +732,9 @@ public:
      */
      */
     template<typename Component, typename... Args>
     template<typename Component, typename... Args>
     Component & accommodate(entity_type entity, Args &&... args) {
     Component & accommodate(entity_type entity, Args &&... args) {
-        assert(valid(entity));
-        auto &cpool = assure<Component>();
-
-        return (cpool.has(entity)
-                ? (cpool.get(entity) = Component{std::forward<Args>(args)...})
-                : cpool.construct(entity, std::forward<Args>(args)...));
+        return (has<Component>(entity)
+                ? replace<Component>(entity, std::forward<Args>(args)...)
+                : assign<Component>(entity, std::forward<Args>(args)...));
     }
     }
 
 
     /**
     /**
@@ -801,8 +759,8 @@ public:
      * @return A temporary sink object.
      * @return A temporary sink object.
      */
      */
     template<typename Component>
     template<typename Component>
-    sink_type<Component> construction() noexcept {
-        return assure<Component>().construction();
+    sink_type construction() noexcept {
+        return (assure<Component>(), std::get<1>(pools[component_family::type<Component>()]).sink());
     }
     }
 
 
     /**
     /**
@@ -827,8 +785,8 @@ public:
      * @return A temporary sink object.
      * @return A temporary sink object.
      */
      */
     template<typename Component>
     template<typename Component>
-    sink_type<Component> destruction() noexcept {
-        return assure<Component>().destruction();
+    sink_type destruction() noexcept {
+        return (assure<Component>(), std::get<2>(pools[component_family::type<Component>()]).sink());
     }
     }
 
 
     /**
     /**
@@ -911,13 +869,11 @@ public:
     template<typename Component>
     template<typename Component>
     void reset(entity_type entity) {
     void reset(entity_type entity) {
         assert(valid(entity));
         assert(valid(entity));
+        auto &cpool = assure<Component>();
 
 
-        if(managed<Component>()) {
-            auto &cpool = pool<Component>();
-
-            if(cpool.has(entity)) {
-                cpool.destroy(entity);
-            }
+        if(cpool.has(entity)) {
+            cpool.destroy(entity);
+            std::get<2>(pools[component_family::type<Component>()]).publish(*this, entity);
         }
         }
     }
     }
 
 
@@ -931,15 +887,15 @@ public:
      */
      */
     template<typename Component>
     template<typename Component>
     void reset() {
     void reset() {
-        if(managed<Component>()) {
-            auto &cpool = pool<Component>();
+        auto &cpool = assure<Component>();
+        auto &sig = std::get<2>(pools[component_family::type<Component>()]);
 
 
-            each([&cpool](auto entity) {
-                if(cpool.has(entity)) {
-                    cpool.destroy(entity);
-                }
-            });
-        }
+        each([&cpool, &sig, this](auto entity) {
+            if(cpool.has(entity)) {
+                cpool.destroy(entity);
+                sig.publish(*this, entity);
+            }
+        });
     }
     }
 
 
     /**
     /**
@@ -1009,7 +965,7 @@ public:
         bool orphan = true;
         bool orphan = true;
 
 
         for(std::size_t i = 0; i < pools.size() && orphan; ++i) {
         for(std::size_t i = 0; i < pools.size() && orphan; ++i) {
-            const auto &pool = pools[i];
+            const auto &pool = std::get<0>(pools[i]);
             orphan = !(pool && pool->has(entity));
             orphan = !(pool && pool->has(entity));
         }
         }
 
 
@@ -1118,12 +1074,13 @@ public:
                 handler->construct(entity);
                 handler->construct(entity);
             }
             }
 
 
-            auto connect = [](auto &pool) {
-                pool.construction().template connect<&Registry::creating<Component...>>();
-                pool.destruction().template connect<&Registry::destroying<Component...>>();
+            auto connect = [this](auto ctype) {
+                auto &cpool = pools[ctype];
+                std::get<1>(cpool).sink().template connect<&Registry::creating<Component...>>();
+                std::get<2>(cpool).sink().template connect<&Registry::destroying<Component...>>();
             };
             };
 
 
-            accumulator_type accumulator = { (connect(assure<Component>()), 0)... };
+            accumulator_type accumulator = { (assure<Component>(), connect(component_family::type<Component>()), 0)... };
             (void)accumulator;
             (void)accumulator;
         }
         }
     }
     }
@@ -1149,13 +1106,14 @@ public:
             using accumulator_type = int[];
             using accumulator_type = int[];
             const auto htype = handler_family::type<Component...>();
             const auto htype = handler_family::type<Component...>();
 
 
-            auto disconnect = [](auto &pool) {
-                pool.construction().template disconnect<&Registry::creating<Component...>>();
-                pool.destruction().template disconnect<&Registry::destroying<Component...>>();
+            auto disconnect = [this](auto ctype) {
+                auto &cpool = pools[ctype];
+                std::get<1>(cpool).sink().template disconnect<&Registry::creating<Component...>>();
+                std::get<2>(cpool).sink().template disconnect<&Registry::destroying<Component...>>();
             };
             };
 
 
             // if a set exists, pools have already been created for it
             // if a set exists, pools have already been created for it
-            accumulator_type accumulator = { (disconnect(pool<Component>()), 0)... };
+            accumulator_type accumulator = { (disconnect(component_family::type<Component>()), 0)... };
             handlers[htype].reset();
             handlers[htype].reset();
             (void)accumulator;
             (void)accumulator;
         }
         }
@@ -1215,7 +1173,7 @@ public:
     PersistentView<Entity, Component...> persistent() {
     PersistentView<Entity, Component...> persistent() {
         prepare<Component...>();
         prepare<Component...>();
         const auto htype = handler_family::type<Component...>();
         const auto htype = handler_family::type<Component...>();
-        return PersistentView<Entity, Component...>{*handlers[htype], pool<Component>()...};
+        return PersistentView<Entity, Component...>{*handlers[htype], assure<Component>()...};
     }
     }
 
 
     /**
     /**
@@ -1269,7 +1227,7 @@ public:
 
 
         raw_fn_type raw = [](const Registry &registry, component_type component) -> const entity_type * {
         raw_fn_type raw = [](const Registry &registry, component_type component) -> const entity_type * {
             const auto &pools = registry.pools;
             const auto &pools = registry.pools;
-            return (component < pools.size() && pools[component]) ? pools[component]->data() : nullptr;
+            return (component < pools.size() && std::get<0>(pools[component])) ? std::get<0>(pools[component])->data() : nullptr;
         };
         };
 
 
         return { *this, seed, available, follow, raw };
         return { *this, seed, available, follow, raw };
@@ -1319,7 +1277,7 @@ public:
 
 
 private:
 private:
     std::vector<std::unique_ptr<SparseSet<Entity>>> handlers;
     std::vector<std::unique_ptr<SparseSet<Entity>>> handlers;
-    std::vector<std::unique_ptr<SparseSet<Entity>>> pools;
+    std::vector<std::tuple<std::unique_ptr<SparseSet<Entity>>, SigH<void(Registry &, Entity)>, SigH<void(Registry &, Entity)>>> pools;
     std::vector<std::unique_ptr<Attachee>> tags;
     std::vector<std::unique_ptr<Attachee>> tags;
     std::vector<entity_type> entities;
     std::vector<entity_type> entities;
     size_type available{};
     size_type available{};