diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index dd50342da..1bbceb326 100644 --- a/src/entt/entity/registry.hpp +++ b/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 struct pool_wrapper: sparse_set { + sigh on_construct; + sigh on_replace; + sigh on_destroy; + basic_registry *owner{}; + void *group{}; + template Component & construct(const Entity entt, Args &&... args) { auto &component = sparse_set::construct(entt, std::forward(args)...); @@ -96,21 +102,16 @@ class basic_registry { std::swap(other, component); return other; } - - sigh on_construct; - sigh on_replace; - sigh on_destroy; - basic_registry *owner; }; template using pool_type = pool_wrapper>; - template - struct non_owning_group; + template + struct group_handler; - template - struct non_owning_group, type_list>: sparse_set { + template + struct group_handler, type_list>: sparse_set { template void maybe_valid_if(basic_registry ®, const Entity entt, const Args &...) { if constexpr(std::disjunction_v...>) { @@ -127,22 +128,17 @@ class basic_registry { } template - 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 + struct group_handler, type_list, Owned...>: sparse_set { + std::size_t owned{}; - template - struct owning_group; - - template - struct owning_group, type_list, Owned...>: boxed_owned { template void maybe_valid_if(basic_registry ®, const Entity entt, const Args &...) { const auto cpools = std::make_tuple(reg.pool()...); @@ -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 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> data; + std::unique_ptr group; bool(* const is_same)(const ENTT_ID_TYPE *); }; @@ -236,17 +225,13 @@ class basic_registry { const auto ctype = type(); if constexpr(is_named_type_v) { - 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 *>(it->pool.get()) - : nullptr; + return it == pools.cend() ? nullptr : static_cast *>(it->pool.get()); } else { - return (ctype < pools.size() && pools[ctype].pool && pools[ctype].runtime_type == ctype) - ? static_cast *>(pools[ctype].pool.get()) - : nullptr; + return (ctype < skip_family_pools && pools[ctype].pool) ? static_cast *>(pools[ctype].pool.get()) : nullptr; } } @@ -261,23 +246,21 @@ class basic_registry { pool_data *pdata = nullptr; if constexpr(is_named_type_v) { - 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 auto * assure(get_t, exclude_t) { - 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, Owned...>; - if constexpr(sizeof...(Owned) == 0) { - const std::size_t extent[] = { sizeof...(Get), sizeof...(Exclude) }; - const ENTT_ID_TYPE types[] = { type()..., type()... }; + const std::size_t extent[] = { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) }; + const ENTT_ID_TYPE types[] = { type()..., type()..., type()... }; + group_type *curr = nullptr; - 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(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(it->group.get()); + } - if(it == outer_groups.cend()) { - using group_type = non_owning_group, type_list>; + if(!curr) { + ENTT_ASSERT(!(owned() || ...)); - non_owning_group_data gdata{ - { sizeof...(Get), sizeof...(Exclude) }, - std::make_unique(), - +[](const ENTT_ID_TYPE *other) { - const std::size_t ctypes[] = { type()..., type()... }; - return std::equal(std::begin(ctypes), std::end(ctypes), other); - } - }; - - auto *curr = static_cast(gdata.data.get()); - const auto cpools = std::make_tuple(assure()..., assure()...); - - (std::get *>(cpools)->on_destroy.sink().template connect<&group_type::template destroy_if<>>(curr), ...); - (std::get *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if>(curr), ...); - - (std::get *>(cpools)->on_destroy.sink().template connect<&group_type::template maybe_valid_if>(curr), ...); - (std::get *>(cpools)->on_construct.sink().template connect<&group_type::template destroy_if>(curr), ...); - - for(const auto entity: view()) { - if(!(has(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(gptr); }}, + +[](const ENTT_ID_TYPE *other) { + const std::size_t ctypes[] = { type()..., type()..., type()... }; + 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()..., type()..., type()... }; - - 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() || ...)); - using group_type = owning_group, type_list, Owned...>; + const auto cpools = std::make_tuple(assure()..., assure()..., assure()...); + curr = static_cast(groups.back().group.get()); - owning_group_data gdata{ - { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) }, - std::make_unique(), - +[](const ENTT_ID_TYPE *other) { - const std::size_t ctypes[] = { type()..., type()..., type()... }; - return std::equal(std::begin(ctypes), std::end(ctypes), other); - }, - +[](ENTT_ID_TYPE ctype) { - return ((ctype == type()) || ...); - } - }; + ((std::get *>(cpools)->group = curr), ...); + (std::get *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if>(curr), ...); + (std::get *>(cpools)->on_destroy.sink().template connect<&group_type::template discard_if<>>(curr), ...); - auto *curr = static_cast(gdata.data.get()); - const auto cpools = std::make_tuple(assure()..., assure()..., assure()...); + (std::get *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if>(curr), ...); + (std::get *>(cpools)->on_destroy.sink().template connect<&group_type::template discard_if<>>(curr), ...); - (std::get *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if>(curr), ...); - (std::get *>(cpools)->on_destroy.sink().template connect<&group_type::template discard_if<>>(curr), ...); + (std::get *>(cpools)->on_destroy.sink().template connect<&group_type::template maybe_valid_if>(curr), ...); + (std::get *>(cpools)->on_construct.sink().template connect<&group_type::template discard_if>(curr), ...); - (std::get *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if>(curr), ...); - (std::get *>(cpools)->on_destroy.sink().template connect<&group_type::template discard_if<>>(curr), ...); + const auto *cpool = std::min({ + static_cast *>(std::get *>(cpools))..., + static_cast *>(std::get *>(cpools))... + }, [](const auto *lhs, const auto *rhs) { + return lhs->size() < rhs->size(); + }); - (std::get *>(cpools)->on_destroy.sink().template connect<&group_type::template maybe_valid_if>(curr), ...); - (std::get *>(cpools)->on_construct.sink().template connect<&group_type::template discard_if>(curr), ...); - - const auto *cpool = std::min({ static_cast *>(std::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 *>(cpools)->has(entity) && ...) - && (std::get *>(cpools)->has(entity) && ...) - && !(std::get *>(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 *>(cpools)->has(entity) && ...) + && (std::get *>(cpools)->has(entity) && ...) + && !(std::get *>(cpools)->has(entity) || ...)) + { + if constexpr(sizeof...(Owned) == 0) { + curr->construct(entity); + } else { const auto pos = curr->owned++; (std::swap(std::get *>(cpools)->get(entity), std::get *>(cpools)->raw()[pos]), ...); (std::get *>(cpools)->swap(std::get *>(cpools)->sparse_set::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 *>(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()...); - return ((std::get *>(cpools) - ? std::get *>(cpools)->has(entity) - : false) && ...); + return ((std::get *>(cpools) ? std::get *>(cpools)->has(entity) : false) && ...); } /** @@ -938,9 +887,7 @@ public: if constexpr(sizeof...(Component) == 1) { const auto cpools = std::make_tuple(pool()...); - return ((std::get *>(cpools) - ? std::get *>(cpools)->try_get(entity) - : nullptr), ...); + return ((std::get *>(cpools) ? std::get *>(cpools)->try_get(entity) : nullptr), ...); } else { return std::tuple *...>{try_get(entity)...}; } @@ -1006,10 +953,7 @@ public: template Component & assign_or_replace(const entity_type entity, Args &&... args) { auto *cpool = assure(); - - return cpool->has(entity) - ? cpool->replace(entity, std::forward(args)...) - : cpool->construct(entity, std::forward(args)...); + return cpool->has(entity) ? cpool->replace(entity, std::forward(args)...) : cpool->construct(entity, std::forward(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 bool owned() const ENTT_NOEXCEPT { - return std::any_of(inner_groups.cbegin(), inner_groups.cend(), [](auto &&gdata) { - return gdata.owned(type()); - }); + const auto *cpool = pool(); + 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) { - 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>()).get(); - } else { - wrapper = it->get(); - } + wrapper = (it == vars.cend()) ? vars.emplace_back(std::make_unique>()).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); + + while(!(ctype < skip_family_vars)) { + vars.emplace(vars.begin()+(skip_family_vars++), nullptr); + } + } + + if(!vars[ctype]) { + vars[ctype] = std::make_unique>(); } wrapper = vars[ctype].get(); - - if(wrapper && wrapper->runtime_type != ctype) { - vars.emplace_back(std::make_unique>()); - std::swap(vars[ctype], vars.back()); - wrapper = vars[ctype].get(); - } else if(!wrapper) { - vars[ctype] = std::make_unique>(); - wrapper = vars[ctype].get(); - } } auto &value = static_cast *>(wrapper)->value; @@ -1651,13 +1594,11 @@ public: const auto ctype = runtime_type(); if constexpr(is_named_type_v) { - 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(); if constexpr(is_named_type_v) { - 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 &>(**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 &>(*vars[ctype]).value : nullptr; } } @@ -1718,9 +1659,10 @@ public: private: std::vector pools; - std::vector inner_groups; - std::vector outer_groups; + std::size_t skip_family_pools{}; + std::vector groups; std::vector> vars; + std::size_t skip_family_vars{}; std::vector entities; size_type available{}; entity_type next{};