registry: added ::destroyed, made ::assign much faster (breaking changes, close #522)
This commit is contained in:
@@ -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};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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]));
|
||||
|
||||
Reference in New Issue
Block a user