registry:

* prevent groups from being used with tombstones
* support to compact one, several or all pools
This commit is contained in:
Michele Caini
2021-06-21 12:59:26 +02:00
parent 5ad0832b22
commit 03511f39b1
2 changed files with 257 additions and 92 deletions

View File

@@ -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};
};

View File

@@ -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,92 +1371,83 @@ 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 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.all_of<int>(e0));
ASSERT_TRUE(registry.all_of<int>(e1));
ASSERT_TRUE(registry.all_of<int>(e2));
const auto view = registry.view<int, char>();
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_FALSE(registry.all_of<int>(e0));
ASSERT_FALSE(registry.all_of<int>(e1));
ASSERT_TRUE(registry.all_of<int>(e2));
}
TEST(Registry, RangeRemove) {
entt::registry registry;
const auto iview = registry.view<int>();
const auto icview = registry.view<int, char>();
@@ -1412,19 +1465,112 @@ TEST(Registry, RangeRemove) {
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, char>(e0);
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.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<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, StableRemove) {
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.any_of<int>(e0));
ASSERT_TRUE(registry.all_of<int>(e1));
ASSERT_TRUE(registry.any_of<int>(e2));
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.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);
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.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) {