Jelajahi Sumber

review: registry internals

Michele Caini 6 tahun lalu
induk
melakukan
ffd864dc72
1 mengubah file dengan 114 tambahan dan 172 penghapusan
  1. 114 172
      src/entt/entity/registry.hpp

+ 114 - 172
src/entt/entity/registry.hpp

@@ -1,4 +1,4 @@
-#ifndef ENTT_ENTITY_REGISTRY_HPP
+#ifndef ENTT_ENTITY_REGISTRY_HPP
 #define ENTT_ENTITY_REGISTRY_HPP
 
 
@@ -63,6 +63,12 @@ class basic_registry {
 
     template<typename Component>
     struct pool_wrapper: sparse_set<Entity, Component> {
+        sigh<void(basic_registry &, const Entity, Component &)> on_construct;
+        sigh<void(basic_registry &, const Entity, Component &)> on_replace;
+        sigh<void(basic_registry &, const Entity)> on_destroy;
+        basic_registry *owner{};
+        void *group{};
+
         template<typename... Args>
         Component & construct(const Entity entt, Args &&... args) {
             auto &component = sparse_set<Entity, Component>::construct(entt, std::forward<Args>(args)...);
@@ -96,21 +102,16 @@ class basic_registry {
             std::swap(other, component);
             return other;
         }
-
-        sigh<void(basic_registry &, const Entity, Component &)> on_construct;
-        sigh<void(basic_registry &, const Entity, Component &)> on_replace;
-        sigh<void(basic_registry &, const Entity)> on_destroy;
-        basic_registry *owner;
     };
 
     template<typename Component>
     using pool_type = pool_wrapper<std::decay_t<Component>>;
 
-    template<typename, typename>
-    struct non_owning_group;
+    template<typename...>
+    struct group_handler;
 
-    template<typename... Get, typename... Exclude>
-    struct non_owning_group<type_list<Exclude...>, type_list<Get...>>: sparse_set<Entity> {
+    template<typename... Exclude, typename... Get>
+    struct group_handler<type_list<Exclude...>, type_list<Get...>>: sparse_set<Entity> {
         template<typename Component, typename... Args>
         void maybe_valid_if(basic_registry &reg, const Entity entt, const Args &...) {
             if constexpr(std::disjunction_v<std::is_same<Get, Component>...>) {
@@ -127,22 +128,17 @@ class basic_registry {
         }
 
         template<typename... Args>
-        void destroy_if(basic_registry &, const Entity entt, const Args &...) {
+        void discard_if(basic_registry &, const Entity entt, const Args &...) {
             if(this->has(entt)) {
                 this->destroy(entt);
             }
         }
     };
 
-    struct boxed_owned {
-        std::size_t owned;
-    };
-
-    template<typename...>
-    struct owning_group;
+    template<typename... Exclude, typename... Get, typename... Owned>
+    struct group_handler<type_list<Exclude...>, type_list<Get...>, Owned...>: sparse_set<Entity> {
+        std::size_t owned{};
 
-    template<typename... Owned, typename... Get, typename... Exclude>
-    struct owning_group<type_list<Exclude...>, type_list<Get...>, Owned...>: boxed_owned {
         template<typename Component, typename... Args>
         void maybe_valid_if(basic_registry &reg, const Entity entt, const Args &...) {
             const auto cpools = std::make_tuple(reg.pool<Owned>()...);
@@ -189,16 +185,9 @@ class basic_registry {
         ENTT_ID_TYPE runtime_type;
     };
 
-    struct owning_group_data {
+    struct group_data {
         const std::size_t extent[3];
-        std::unique_ptr<boxed_owned> data;
-        bool(* const is_same)(const ENTT_ID_TYPE *);
-        bool(* const owned)(ENTT_ID_TYPE);
-    };
-
-    struct non_owning_group_data {
-        const std::size_t extent[2];
-        std::unique_ptr<sparse_set<Entity>> data;
+        std::unique_ptr<void, void(*)(void *)> group;
         bool(* const is_same)(const ENTT_ID_TYPE *);
     };
 
@@ -236,17 +225,13 @@ class basic_registry {
         const auto ctype = type<Component>();
 
         if constexpr(is_named_type_v<Component>) {
-            const auto it = std::find_if(pools.begin(), pools.end(), [ctype](const auto &candidate) {
-                return candidate.pool && candidate.runtime_type == ctype;
+            const auto it = std::find_if(pools.begin()+skip_family_pools, pools.end(), [ctype](const auto &candidate) {
+                return candidate.runtime_type == ctype;
             });
 
-            return (it != pools.cend() && it->pool)
-                    ? static_cast<const pool_type<Component> *>(it->pool.get())
-                    : nullptr;
+            return it == pools.cend() ? nullptr : static_cast<const pool_type<Component> *>(it->pool.get());
         } else {
-            return (ctype < pools.size() && pools[ctype].pool && pools[ctype].runtime_type == ctype)
-                    ? static_cast<const pool_type<Component> *>(pools[ctype].pool.get())
-                    : nullptr;
+            return (ctype < skip_family_pools && pools[ctype].pool) ? static_cast<const pool_type<Component> *>(pools[ctype].pool.get()) : nullptr;
         }
     }
 
@@ -261,23 +246,21 @@ class basic_registry {
         pool_data *pdata = nullptr;
 
         if constexpr(is_named_type_v<Component>) {
-            const auto it = std::find_if(pools.begin(), pools.end(), [ctype](const auto &candidate) {
-                return candidate.pool && candidate.runtime_type == ctype;
+            const auto it = std::find_if(pools.begin()+skip_family_pools, pools.end(), [ctype](const auto &candidate) {
+                return candidate.runtime_type == ctype;
             });
 
             pdata = (it == pools.cend() ? &pools.emplace_back() : &(*it));
         } else {
-            if(!(ctype < pools.size())) {
-                pools.resize(ctype+1);
+            if(!(ctype < skip_family_pools)) {
+                pools.reserve(pools.size()+ctype-skip_family_pools+1);
+
+                while(!(ctype < skip_family_pools)) {
+                    pools.emplace(pools.begin()+(skip_family_pools++), pool_data{});
+                }
             }
 
             pdata = &pools[ctype];
-
-            if(pdata->pool && pdata->runtime_type != ctype) {
-                pools.emplace_back();
-                std::swap(pools[ctype], pools.back());
-                pdata = &pools[ctype];
-            }
         }
 
         if(!pdata->pool) {
@@ -291,106 +274,74 @@ class basic_registry {
 
     template<typename... Owned, typename... Get, typename... Exclude>
     auto * assure(get_t<Get...>, exclude_t<Exclude...>) {
-        static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1);
         static_assert(sizeof...(Owned) + sizeof...(Get) > 0);
+        static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1);
+        using group_type = group_handler<type_list<Exclude...>, type_list<Get...>, Owned...>;
 
-        if constexpr(sizeof...(Owned) == 0) {
-            const std::size_t extent[] = { sizeof...(Get), sizeof...(Exclude) };
-            const ENTT_ID_TYPE types[] = { type<Get>()..., type<Exclude>()... };
-
-            auto it = std::find_if(outer_groups.begin(), outer_groups.end(), [&extent, &types](auto &&gdata) {
-                return std::equal(std::begin(extent), std::end(extent), gdata.extent) && gdata.is_same(types);
-            });
-
-            if(it == outer_groups.cend()) {
-                using group_type = non_owning_group<type_list<Exclude...>, type_list<Get...>>;
-
-                non_owning_group_data gdata{
-                    { sizeof...(Get), sizeof...(Exclude) },
-                    std::make_unique<group_type>(),
-                    +[](const ENTT_ID_TYPE *other) {
-                        const std::size_t ctypes[] = { type<Get>()..., type<Exclude>()... };
-                        return std::equal(std::begin(ctypes), std::end(ctypes), other);
-                    }
-                };
-
-                auto *curr = static_cast<group_type *>(gdata.data.get());
-                const auto cpools = std::make_tuple(assure<Get>()..., assure<Exclude>()...);
+        const std::size_t extent[] = { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) };
+        const ENTT_ID_TYPE types[] = { type<Owned>()..., type<Get>()..., type<Exclude>()... };
+        group_type *curr = nullptr;
 
-                (std::get<pool_type<Get> *>(cpools)->on_destroy.sink().template connect<&group_type::template destroy_if<>>(curr), ...);
-                (std::get<pool_type<Get> *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if<Get, Get>>(curr), ...);
+        if(auto it = std::find_if(groups.begin(), groups.end(), [&extent, &types](auto &&gdata) {
+            return std::equal(std::begin(extent), std::end(extent), gdata.extent) && gdata.is_same(types);
+        }); it != groups.cend())
+        {
+            curr = static_cast<group_type *>(it->group.get());
+        }
 
-                (std::get<pool_type<Exclude> *>(cpools)->on_destroy.sink().template connect<&group_type::template maybe_valid_if<Exclude>>(curr), ...);
-                (std::get<pool_type<Exclude> *>(cpools)->on_construct.sink().template connect<&group_type::template destroy_if<Exclude>>(curr), ...);
+        if(!curr) {
+            ENTT_ASSERT(!(owned<Owned>() || ...));
 
-                for(const auto entity: view<Get...>()) {
-                    if(!(has<Exclude>(entity) || ...)) {
-                        curr->construct(entity);
-                    }
+            groups.push_back(group_data{
+                { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) },
+                decltype(group_data::group){new group_type, +[](void *gptr) { delete static_cast<group_type *>(gptr); }},
+                +[](const ENTT_ID_TYPE *other) {
+                    const std::size_t ctypes[] = { type<Owned>()..., type<Get>()..., type<Exclude>()... };
+                    return std::equal(std::begin(ctypes), std::end(ctypes), other);
                 }
-
-                outer_groups.push_back(std::move(gdata));
-                it = std::prev(outer_groups.end());
-            }
-
-            return it->data.get();
-        } else {
-            const std::size_t extent[] = { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) };
-            const ENTT_ID_TYPE types[] = { type<Owned>()..., type<Get>()..., type<Exclude>()... };
-
-            auto it = std::find_if(inner_groups.begin(), inner_groups.end(), [&extent, &types](auto &&gdata) {
-                return std::equal(std::begin(extent), std::end(extent), gdata.extent) && gdata.is_same(types);
             });
 
-            if(it == inner_groups.cend()) {
-                ENTT_ASSERT(!(owned<Owned>() || ...));
-                using group_type = owning_group<type_list<Exclude...>, type_list<Get...>, Owned...>;
-
-                owning_group_data gdata{
-                    { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) },
-                    std::make_unique<group_type>(),
-                    +[](const ENTT_ID_TYPE *other) {
-                        const std::size_t ctypes[] = { type<Owned>()..., type<Get>()..., type<Exclude>()... };
-                        return std::equal(std::begin(ctypes), std::end(ctypes), other);
-                    },
-                    +[](ENTT_ID_TYPE ctype) {
-                        return ((ctype == type<Owned>()) || ...);
-                    }
-                };
+            const auto cpools = std::make_tuple(assure<Owned>()..., assure<Get>()..., assure<Exclude>()...);
+            curr = static_cast<group_type *>(groups.back().group.get());
 
-                auto *curr = static_cast<group_type *>(gdata.data.get());
-                const auto cpools = std::make_tuple(assure<Owned>()..., assure<Get>()..., assure<Exclude>()...);
+            ((std::get<pool_type<Owned> *>(cpools)->group = curr), ...);
+            (std::get<pool_type<Owned> *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if<Owned, Owned>>(curr), ...);
+            (std::get<pool_type<Owned> *>(cpools)->on_destroy.sink().template connect<&group_type::template discard_if<>>(curr), ...);
 
-                (std::get<pool_type<Owned> *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if<Owned, Owned>>(curr), ...);
-                (std::get<pool_type<Owned> *>(cpools)->on_destroy.sink().template connect<&group_type::template discard_if<>>(curr), ...);
+            (std::get<pool_type<Get> *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if<Get, Get>>(curr), ...);
+            (std::get<pool_type<Get> *>(cpools)->on_destroy.sink().template connect<&group_type::template discard_if<>>(curr), ...);
 
-                (std::get<pool_type<Get> *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if<Get, Get>>(curr), ...);
-                (std::get<pool_type<Get> *>(cpools)->on_destroy.sink().template connect<&group_type::template discard_if<>>(curr), ...);
+            (std::get<pool_type<Exclude> *>(cpools)->on_destroy.sink().template connect<&group_type::template maybe_valid_if<Exclude>>(curr), ...);
+            (std::get<pool_type<Exclude> *>(cpools)->on_construct.sink().template connect<&group_type::template discard_if<Exclude>>(curr), ...);
 
-                (std::get<pool_type<Exclude> *>(cpools)->on_destroy.sink().template connect<&group_type::template maybe_valid_if<Exclude>>(curr), ...);
-                (std::get<pool_type<Exclude> *>(cpools)->on_construct.sink().template connect<&group_type::template discard_if<Exclude>>(curr), ...);
-
-                const auto *cpool = std::min({ static_cast<sparse_set<entity_type> *>(std::get<pool_type<Owned> *>(cpools))... }, [](const auto *lhs, const auto *rhs) {
-                    return lhs->size() < rhs->size();
-                });
+            const auto *cpool = std::min({
+                static_cast<sparse_set<Entity> *>(std::get<pool_type<Owned> *>(cpools))...,
+                static_cast<sparse_set<Entity> *>(std::get<pool_type<Get> *>(cpools))...
+            }, [](const auto *lhs, const auto *rhs) {
+                return lhs->size() < rhs->size();
+            });
 
-                // we cannot iterate backwards because we want to leave behind valid entities
-                std::for_each(cpool->data(), cpool->data() + cpool->size(), [curr, &cpools](const auto entity) {
-                    if((std::get<pool_type<Owned> *>(cpools)->has(entity) && ...)
-                            && (std::get<pool_type<Get> *>(cpools)->has(entity) && ...)
-                            && !(std::get<pool_type<Exclude> *>(cpools)->has(entity) || ...))
-                    {
+            // we cannot iterate backwards because we want to leave behind valid entities in case of owned types
+            std::for_each(cpool->data(), cpool->data() + cpool->size(), [curr, &cpools](const auto entity) {
+                if((std::get<pool_type<Owned> *>(cpools)->has(entity) && ...)
+                        && (std::get<pool_type<Get> *>(cpools)->has(entity) && ...)
+                        && !(std::get<pool_type<Exclude> *>(cpools)->has(entity) || ...))
+                {
+                    if constexpr(sizeof...(Owned) == 0) {
+                        curr->construct(entity);
+                    } else {
                         const auto pos = curr->owned++;
                         (std::swap(std::get<pool_type<Owned> *>(cpools)->get(entity), std::get<pool_type<Owned> *>(cpools)->raw()[pos]), ...);
                         (std::get<pool_type<Owned> *>(cpools)->swap(std::get<pool_type<Owned> *>(cpools)->sparse_set<Entity>::get(entity), pos), ...);
                     }
-                });
-
-                inner_groups.push_back(std::move(gdata));
-                it = std::prev(inner_groups.end());
-            }
+                }
+            });
+        }
 
-            return &it->data->owned;
+        if constexpr(sizeof...(Owned) == 0) {
+            return static_cast<sparse_set<Entity> *>(curr);
+        } else {
+            return &curr->owned;
         }
     }
 
@@ -848,9 +799,7 @@ public:
     bool has(const entity_type entity) const ENTT_NOEXCEPT {
         ENTT_ASSERT(valid(entity));
         [[maybe_unused]] const auto cpools = std::make_tuple(pool<Component>()...);
-        return ((std::get<const pool_type<Component> *>(cpools)
-                 ? std::get<const pool_type<Component> *>(cpools)->has(entity)
-                 : false) && ...);
+        return ((std::get<const pool_type<Component> *>(cpools) ? std::get<const pool_type<Component> *>(cpools)->has(entity) : false) && ...);
     }
 
     /**
@@ -938,9 +887,7 @@ public:
 
         if constexpr(sizeof...(Component) == 1) {
             const auto cpools = std::make_tuple(pool<Component>()...);
-            return ((std::get<const pool_type<Component> *>(cpools)
-                     ? std::get<const pool_type<Component> *>(cpools)->try_get(entity)
-                     : nullptr), ...);
+            return ((std::get<const pool_type<Component> *>(cpools) ? std::get<const pool_type<Component> *>(cpools)->try_get(entity) : nullptr), ...);
         } else {
             return std::tuple<std::add_const_t<Component> *...>{try_get<Component>(entity)...};
         }
@@ -1006,10 +953,7 @@ public:
     template<typename Component, typename... Args>
     Component & assign_or_replace(const entity_type entity, Args &&... args) {
         auto *cpool = assure<Component>();
-
-        return cpool->has(entity)
-                ? cpool->replace(entity, std::forward<Args>(args)...)
-                : cpool->construct(entity, std::forward<Args>(args)...);
+        return cpool->has(entity) ? cpool->replace(entity, std::forward<Args>(args)...) : cpool->construct(entity, std::forward<Args>(args)...);
     }
 
     /**
@@ -1293,7 +1237,7 @@ public:
         ENTT_ASSERT(valid(entity));
         bool orphan = true;
 
-        for(std::size_t i = {}; i < pools.size() && orphan; ++i) {
+        for(std::size_t i = {}, last = pools.size(); i < last && orphan; ++i) {
             const auto &pdata = pools[i];
             orphan = !(pdata.pool && pdata.pool->has(entity));
         }
@@ -1378,9 +1322,8 @@ public:
      */
     template<typename Component>
     bool owned() const ENTT_NOEXCEPT {
-        return std::any_of(inner_groups.cbegin(), inner_groups.cend(), [](auto &&gdata) {
-            return gdata.owned(type<Component>());
-        });
+        const auto *cpool = pool<Component>();
+        return cpool && cpool->group;
     }
 
     /**
@@ -1515,9 +1458,14 @@ public:
             }
         }
 
-        other.next = next;
-        other.available = available;
+        other.skip_family_pools = skip_family_pools;
         other.entities = entities;
+        other.available = available;
+        other.next = next;
+
+        other.pools.erase(std::remove_if(other.pools.begin()+skip_family_pools, other.pools.end(), [](const auto &pdata) {
+            return !pdata.pool;
+        }), other.pools.end());
 
         return other;
     }
@@ -1609,30 +1557,25 @@ public:
         ctx_wrapper *wrapper = nullptr;
 
         if constexpr(is_named_type_v<Type>) {
-            const auto it = std::find_if(vars.begin(), vars.end(), [ctype](const auto &candidate) {
-                return candidate && candidate->runtime_type == ctype;
+            const auto it = std::find_if(vars.begin()+skip_family_vars, vars.end(), [ctype](const auto &candidate) {
+                return candidate->runtime_type == ctype;
             });
 
-            if(it == vars.cend()) {
-                wrapper = vars.emplace_back(std::make_unique<type_wrapper<Type>>()).get();
-            } else {
-                wrapper = it->get();
-            }
+            wrapper = (it == vars.cend()) ? vars.emplace_back(std::make_unique<type_wrapper<Type>>()).get() : it->get();
         } else {
-            if(!(ctype < vars.size())) {
-                vars.resize(ctype+1);
-            }
+            if(!(ctype < skip_family_vars)) {
+                vars.reserve(vars.size()+ctype-skip_family_vars+1);
 
-            wrapper = vars[ctype].get();
+                while(!(ctype < skip_family_vars)) {
+                    vars.emplace(vars.begin()+(skip_family_vars++), nullptr);
+                }
+            }
 
-            if(wrapper && wrapper->runtime_type != ctype) {
-                vars.emplace_back(std::make_unique<type_wrapper<Type>>());
-                std::swap(vars[ctype], vars.back());
-                wrapper = vars[ctype].get();
-            } else if(!wrapper) {
+            if(!vars[ctype]) {
                 vars[ctype] = std::make_unique<type_wrapper<Type>>();
-                wrapper = vars[ctype].get();
             }
+
+            wrapper = vars[ctype].get();
         }
 
         auto &value = static_cast<type_wrapper<Type> *>(wrapper)->value;
@@ -1651,13 +1594,11 @@ public:
         const auto ctype = runtime_type<Type, context_family>();
 
         if constexpr(is_named_type_v<Type>) {
-            for(auto &&wrapper: vars) {
-                if(wrapper && wrapper->runtime_type == ctype) {
-                    wrapper.reset();
-                }
-            }
+            vars.erase(std::remove_if(vars.begin()+skip_family_vars, vars.end(), [ctype](auto &wrapper) {
+                return wrapper->runtime_type == ctype;
+            }), vars.end());
         } else {
-            if(ctype < vars.size()) {
+            if(ctype < skip_family_vars) {
                 vars[ctype].reset();
             }
         }
@@ -1674,13 +1615,13 @@ public:
         const auto ctype = runtime_type<Type, context_family>();
 
         if constexpr(is_named_type_v<Type>) {
-            const auto it = std::find_if(vars.begin(), vars.end(), [ctype](const auto &candidate) {
-                return candidate && candidate->runtime_type == ctype;
+            const auto it = std::find_if(vars.begin()+skip_family_vars, vars.end(), [ctype](const auto &candidate) {
+                return candidate->runtime_type == ctype;
             });
 
             return (it == vars.cend()) ? nullptr : &static_cast<const type_wrapper<Type> &>(**it).value;
         } else {
-            const bool valid = ctype < vars.size() && vars[ctype] && vars[ctype]->runtime_type == ctype;
+            const bool valid = ctype < skip_family_vars && vars[ctype];
             return valid ? &static_cast<const type_wrapper<Type> &>(*vars[ctype]).value : nullptr;
         }
     }
@@ -1718,9 +1659,10 @@ public:
 
 private:
     std::vector<pool_data> pools;
-    std::vector<owning_group_data> inner_groups;
-    std::vector<non_owning_group_data> outer_groups;
+    std::size_t skip_family_pools{};
+    std::vector<group_data> groups;
     std::vector<std::unique_ptr<ctx_wrapper>> vars;
+    std::size_t skip_family_vars{};
     std::vector<entity_type> entities;
     size_type available{};
     entity_type next{};