registry: added ::destroyed, made ::assign much faster (breaking changes, close #522)

This commit is contained in:
Michele Caini
2020-10-27 23:01:28 +01:00
parent 86808f5035
commit 2d4b00ec24
3 changed files with 50 additions and 33 deletions

View File

@@ -134,17 +134,17 @@ class basic_registry {
}
Entity recycle_identifier() {
ENTT_ASSERT(destroyed != null);
const auto curr = to_integral(destroyed);
ENTT_ASSERT(available != null);
const auto curr = to_integral(available);
const auto version = to_integral(entities[curr]) & (traits_type::version_mask << traits_type::entity_shift);
destroyed = entity_type{to_integral(entities[curr]) & traits_type::entity_mask};
available = entity_type{to_integral(entities[curr]) & traits_type::entity_mask};
return entities[curr] = entity_type{curr | version};
}
void release_entity(const Entity entity, const typename traits_type::version_type version) {
const auto entt = to_integral(entity) & traits_type::entity_mask;
entities[entt] = entity_type{to_integral(destroyed) | (typename traits_type::entity_type{version} << traits_type::entity_shift)};
destroyed = entity_type{entt};
entities[entt] = entity_type{to_integral(available) | (typename traits_type::entity_type{version} << traits_type::entity_shift)};
available = entity_type{entt};
}
public:
@@ -198,7 +198,7 @@ public:
*/
[[nodiscard]] size_type alive() const {
auto sz = entities.size();
auto curr = destroyed;
auto curr = available;
for(; curr != null; --sz) {
curr = entities[to_integral(curr) & traits_type::entity_mask];
@@ -340,6 +340,18 @@ public:
return entities.data();
}
/**
* @brief Returns the head of the list of destroyed entities.
*
* This function is intended for use in conjunction with `assign`.<br/>
* The returned entity has an invalid identifier in all cases.
*
* @return The head of the list of destroyed entities.
*/
[[nodiscard]] entity_type destroyed() const ENTT_NOEXCEPT {
return available;
}
/**
* @brief Checks if an entity identifier refers to a valid entity.
* @param entity An entity identifier, either valid or not.
@@ -398,7 +410,7 @@ public:
* @return A valid entity identifier.
*/
entity_type create() {
return destroyed == null ? generate_identifier() : recycle_identifier();
return available == null ? generate_identifier() : recycle_identifier();
}
/**
@@ -427,7 +439,7 @@ public:
} else if(const auto curr = (to_integral(entities[req]) & traits_type::entity_mask); req == curr) {
entt = create();
} else {
auto *it = &destroyed;
auto *it = &available;
for(; (to_integral(*it) & traits_type::entity_mask) != req; it = &entities[to_integral(*it) & traits_type::entity_mask]);
*it = entity_type{curr | (to_integral(*it) & (traits_type::version_mask << traits_type::entity_shift))};
entt = entities[req] = hint;
@@ -447,7 +459,7 @@ public:
*/
template<typename It>
void create(It first, It last) {
for(; destroyed != null && first != last; ++first) {
for(; available != null && first != last; ++first) {
*first = recycle_identifier();
}
@@ -459,9 +471,11 @@ public:
/**
* @brief Assigns entities to an empty registry.
*
* This function is intended for use in conjunction with `raw`.<br/>
* Don't try to inject ranges of randomly generated entities. There is no
* guarantee that a registry will continue to work properly in this case.
* This function is intended for use in conjunction with `raw` and
* `assign`.<br/>
* Don't try to inject ranges of randomly generated entities nor the _wrong_
* head for the list of destroyed entities. There is no guarantee that a
* registry will continue to work properly in this case.
*
* @warning
* An assertion will abort the execution at runtime in debug mode if all
@@ -470,18 +484,13 @@ public:
* @tparam It Type of input iterator.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param destroyed The head of the list of destroyed entities.
*/
template<typename It>
void assign(It first, It last) {
void assign(It first, It last, const entity_type destroyed) {
ENTT_ASSERT(std::all_of(pools.cbegin(), pools.cend(), [](auto &&pdata) { return !pdata.pool || pdata.pool->empty(); }));
entities.assign(first, last);
destroyed = null;
for(std::size_t pos{}, end = entities.size(); pos < end; ++pos) {
if((to_integral(entities[pos]) & traits_type::entity_mask) != pos) {
release_entity(entity_type{static_cast<typename traits_type::entity_type>(pos)}, version(entities[pos]));
}
}
available = destroyed;
}
/**
@@ -963,7 +972,7 @@ public:
*/
template<typename Func>
void each(Func func) const {
if(destroyed == null) {
if(available == null) {
for(auto pos = entities.size(); pos; --pos) {
func(entities[pos-1]);
}
@@ -1633,7 +1642,7 @@ private:
mutable std::vector<pool_data> pools{};
std::vector<entity_type> entities{};
std::vector<variable_data> vars{};
entity_type destroyed{null};
entity_type available{null};
};

View File

@@ -95,15 +95,15 @@ public:
template<typename Archive>
const basic_snapshot & entities(Archive &archive) const {
const auto sz = reg->size();
auto first = reg->data();
const auto last = first + sz;
archive(typename traits_type::entity_type(sz));
while(first != last) {
archive(*(first++));
for(auto first = reg->data(), last = first + sz; first != last; ++first) {
archive(*first);
}
archive(reg->destroyed());
return *this;
}
@@ -234,7 +234,10 @@ public:
archive(all[pos]);
}
reg->assign(all.cbegin(), all.cend());
entity_type destroyed;
archive(destroyed);
reg->assign(all.cbegin(), all.cend(), destroyed);
return *this;
}
@@ -302,12 +305,10 @@ class basic_continuous_loader {
using traits_type = entt_traits<Entity>;
void destroy(Entity entt) {
const auto it = remloc.find(entt);
if(it == remloc.cend()) {
if(const auto it = remloc.find(entt); it == remloc.cend()) {
const auto local = reg->create();
remloc.emplace(entt, std::make_pair(local, true));
reg->destroy(local);
reg->destroy(local, 0u);
}
}
@@ -318,7 +319,10 @@ class basic_continuous_loader {
const auto local = reg->create();
remloc.emplace(entt, std::make_pair(local, true));
} else {
remloc[entt].first = reg->valid(remloc[entt].first) ? remloc[entt].first : reg->create();
if(!reg->valid(remloc[entt].first)) {
remloc[entt].first = reg->create();
}
// set the dirty flag
remloc[entt].second = true;
}
@@ -451,6 +455,9 @@ public:
}
}
// discards the head of the list of destroyed entities
archive(entt);
return *this;
}

View File

@@ -1404,7 +1404,8 @@ TEST(Registry, AssignEntities) {
registry.destroy(entities[2]);
entt::registry other;
other.assign(registry.data(), registry.data() + registry.size());
const auto *data = registry.data();
other.assign(data, data + registry.size(), registry.destroyed());
ASSERT_EQ(registry.size(), other.size());
ASSERT_TRUE(other.valid(entities[0]));