Michele Caini 8 лет назад
Родитель
Сommit
29de6d89d4
3 измененных файлов с 87 добавлено и 121 удалено
  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 |
 |-----------|-------------|-------------|
-| 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, 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** |
@@ -461,8 +461,8 @@ velocity.dx = 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
 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.
 * 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
 
 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
 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
 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
@@ -802,10 +809,10 @@ Example of use:
 InputArchive input;
 
 registry.restore()
-    .entities()
-    .destroyed()
-    .component<AComponent, AnotherComponent>(output)
-    .tag<MyTag>(output)
+    .entities(input)
+    .destroyed(input)
+    .component<AComponent, AnotherComponent>(input)
+    .tag<MyTag>(input)
     .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,
   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
   destroyed during iterations.<br/>
   `EnTT` partially solves the problem with a few limitations:
 
   * 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
   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 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)
+* 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)
 * dictionary based dependency class (templates copied over) + prefabs (shared state/copy-on-write)
 * "singleton mode" for tags (see #66)

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

@@ -59,6 +59,8 @@ class Registry {
     }
 
     struct Attachee {
+        Attachee(Entity entity): entity{entity} {}
+        virtual ~Attachee() = default;
         Entity entity;
     };
 
@@ -74,69 +76,20 @@ class Registry {
     };
 
     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>();
 
         if(!(ctype < pools.size())) {
             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:
@@ -150,10 +103,8 @@ public:
     using tag_type = typename tag_family::family_type;
     /*! @brief Unsigned integer type. */
     using component_type = typename component_family::family_type;
-
     /*! @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. */
     Registry() = default;
@@ -209,7 +160,8 @@ public:
      */
     template<typename Component>
     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>
     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;
         ++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)) {
                 cpool->destroy(entity);
+                std::get<2>(pools[pos-1]).publish(*this, entity);
             }
-        });
+        }
     }
 
     /**
@@ -466,7 +423,10 @@ public:
     template<typename Component, typename... Args>
     Component & assign(entity_type entity, Args &&... args) {
         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>
     void remove(entity_type 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));
         using accumulator_type = bool[];
         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;
     }
 
@@ -588,7 +550,9 @@ public:
     template<typename Component>
     const Component & get(entity_type entity) const noexcept {
         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
      *
-     * Prefer this function anyway because it has slightly better
-     * performance.
-     *
      * @warning
      * 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
@@ -771,12 +732,9 @@ public:
      */
     template<typename Component, typename... 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.
      */
     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.
      */
     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>
     void reset(entity_type 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>
     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;
 
         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));
         }
 
@@ -1118,12 +1074,13 @@ public:
                 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;
         }
     }
@@ -1149,13 +1106,14 @@ public:
             using accumulator_type = int[];
             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
-            accumulator_type accumulator = { (disconnect(pool<Component>()), 0)... };
+            accumulator_type accumulator = { (disconnect(component_family::type<Component>()), 0)... };
             handlers[htype].reset();
             (void)accumulator;
         }
@@ -1215,7 +1173,7 @@ public:
     PersistentView<Entity, Component...> persistent() {
         prepare<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 * {
             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 };
@@ -1319,7 +1277,7 @@ public:
 
 private:
     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<entity_type> entities;
     size_type available{};