From 4ac46bf19da0da32f2130d799518562ac3ecbf80 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Thu, 7 May 2020 00:55:48 +0200 Subject: [PATCH] snapshot: registry friendship is no longer required --- TODO | 9 +- src/entt/entity/registry.hpp | 43 +------- src/entt/entity/snapshot.hpp | 190 ++++++++++++++++------------------- 3 files changed, 93 insertions(+), 149 deletions(-) diff --git a/TODO b/TODO index 57113cb6a..e4f76d17d 100644 --- a/TODO +++ b/TODO @@ -16,12 +16,11 @@ Next: * make it easier to hook into the type system and describe how to do that to eg auto-generate meta types on first use * review multi component views to reduce instantiations once empty types are gone... * add observer functions aside observer class +* registry clear can use the range-destroy and run much, much faster! * WIP: - introduce the component iterators for non-contiguous collections of entities (multi component views, observers, user defined collections) - - deprecate snapshot, loader, ... - - provide documentation to describe alternatives -* WIP: snapshot rework/deprecation - - remove snapshot/loader from registry, make them external (faster) tools - - deprecate snapshot classes, update documentation to describe alternatives +* WIP: snapshot rework + - snapshot: use entity injection to load all entities at once + - update documentation to describe alternatives diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index a916ed065..0867a96aa 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -1580,20 +1580,9 @@ public: * * @return A temporary object to use to take snasphosts. */ + [[deprecated("basic_snapshot has now a constructor that accepts a reference to a registry")]] entt::basic_snapshot snapshot() const { - using follow_fn_type = entity_type(const basic_registry &, const entity_type); - - const auto head = to_integral(destroyed); - const entity_type seed = (destroyed == null) ? destroyed : entity_type{head | (to_integral(entities[head]) & (traits_type::version_mask << traits_type::entity_shift))}; - - follow_fn_type *follow = [](const basic_registry ®, const entity_type entity) -> entity_type { - const auto &others = reg.entities; - const auto entt = to_integral(entity) & traits_type::entity_mask; - const auto curr = to_integral(others[entt]) & traits_type::entity_mask; - return entity_type{curr | (to_integral(others[curr]) & (traits_type::version_mask << traits_type::entity_shift))}; - }; - - return { this, seed, follow }; + return { *this }; } /** @@ -1611,33 +1600,9 @@ public: * * @return A temporary object to use to load snasphosts. */ + [[deprecated("basic_snapshot_loader has now a constructor that accepts a reference to a registry")]] basic_snapshot_loader loader() { - using force_fn_type = void(basic_registry &, const entity_type, const bool); - - force_fn_type *force = [](basic_registry ®, const entity_type entity, const bool drop) { - const auto entt = to_integral(entity) & traits_type::entity_mask; - auto &others = reg.entities; - - if(!(entt < others.size())) { - auto curr = others.size(); - others.resize(entt + 1); - std::generate(others.data() + curr, others.data() + entt, [&curr]() { return entity_type(curr++); }); - } - - others[entt] = entity; - - if(drop) { - reg.destroy(entity); - const auto version = to_integral(entity) & (traits_type::version_mask << traits_type::entity_shift); - others[entt] = entity_type{(to_integral(others[entt]) & traits_type::entity_mask) | version}; - } - }; - - clear(); - entities.clear(); - destroyed = null; - - return { this, force }; + return { *this }; } /** diff --git a/src/entt/entity/snapshot.hpp b/src/entt/entity/snapshot.hpp index 25daec7da..3f7eb87fc 100644 --- a/src/entt/entity/snapshot.hpp +++ b/src/entt/entity/snapshot.hpp @@ -3,6 +3,7 @@ #include +#include #include #include #include @@ -31,15 +32,8 @@ class basic_snapshot { /*! @brief A registry is allowed to create snapshots. */ friend class basic_registry; - using follow_fn_type = Entity(const basic_registry &, const Entity); using traits_type = entt_traits>; - basic_snapshot(const basic_registry *source, Entity init, follow_fn_type *fn) ENTT_NOEXCEPT - : reg{source}, - seed{init}, - follow{fn} - {} - template void get(Archive &archive, std::size_t sz, It first, It last) const { archive(typename traits_type::entity_type(sz)); @@ -71,6 +65,17 @@ class basic_snapshot { } public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot(const basic_registry &source) ENTT_NOEXCEPT + : reg{&source} + {} + /*! @brief Default move constructor. */ basic_snapshot(basic_snapshot &&) = default; @@ -78,10 +83,10 @@ public: basic_snapshot & operator=(basic_snapshot &&) = default; /** - * @brief Puts aside all the entities that are still in use. + * @brief Puts aside all the entities from the underlying registry. * * Entities are serialized along with their versions. Destroyed entities are - * not taken in consideration by this function. + * taken in consideration as well by this function. * * @tparam Archive Type of output archive. * @param archive A valid reference to an output archive. @@ -89,39 +94,28 @@ public: */ template const basic_snapshot & entities(Archive &archive) const { - archive(typename traits_type::entity_type(reg->alive())); - reg->each([&archive](const auto entt) { archive(entt); }); - return *this; - } + const auto sz = reg->size(); + auto first = reg->data(); + const auto last = first + sz; - /** - * @brief Puts aside destroyed entities. - * - * Entities are serialized along with their versions. Entities that are - * still in use are not taken in consideration by this function. - * - * @tparam Archive Type of output archive. - * @param archive A valid reference to an output archive. - * @return An object of this type to continue creating the snapshot. - */ - template - const basic_snapshot & destroyed(Archive &archive) const { - auto size = reg->size() - reg->alive(); - archive(typename traits_type::entity_type(size)); + archive(typename traits_type::entity_type(sz)); - if(size) { - auto curr = seed; - archive(curr); - - for(--size; size; --size) { - curr = follow(*reg, curr); - archive(curr); - } + while(first != last) { + archive(*(first++)); } return *this; } + /** + * @brief Deprecated function. Currently, it does nothing. + * @tparam Archive Type of output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + [[deprecated("use ::entities instead, it exports now also destroyed entities")]] + const basic_snapshot & destroyed(Archive &) const { return *this; } + /** * @brief Puts aside the given components. * @@ -160,9 +154,7 @@ public: } private: - const basic_registry *reg; - const Entity seed; - follow_fn_type *follow; + const basic_registry *reg; }; @@ -181,36 +173,15 @@ class basic_snapshot_loader { /*! @brief A registry is allowed to create snapshot loaders. */ friend class basic_registry; - using force_fn_type = void(basic_registry &, const Entity, const bool); using traits_type = entt_traits>; - basic_snapshot_loader(basic_registry *source, force_fn_type *fn) ENTT_NOEXCEPT - : reg{source}, - force{fn} - { - // to restore a snapshot as a whole requires a clean registry - ENTT_ASSERT(reg->empty()); - } - - template - void assure(Archive &archive, bool discard) const { - typename traits_type::entity_type length{}; - archive(length); - - while(length--) { - Entity entt{}; - archive(entt); - force(*reg, entt, discard); - } - } - template void assign(Archive &archive, Args... args) const { typename traits_type::entity_type length{}; archive(length); while(length--) { - Entity entt{}; + entity_type entt{}; if constexpr(std::is_empty_v) { archive(entt); @@ -228,6 +199,20 @@ class basic_snapshot_loader { } public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot_loader(basic_registry &source) ENTT_NOEXCEPT + : reg{&source} + { + // restoring a snapshot as a whole requires a clean registry + ENTT_ASSERT(reg->empty()); + } + /*! @brief Default move constructor. */ basic_snapshot_loader(basic_snapshot_loader &&) = default; @@ -246,27 +231,28 @@ public: */ template const basic_snapshot_loader & entities(Archive &archive) const { - static constexpr auto discard = false; - assure(archive, discard); + typename traits_type::entity_type length{}; + + archive(length); + std::vector entities(length); + + for(decltype(length) pos{}; pos < length; ++pos) { + archive(entities[pos]); + } + + reg->assign(entities.cbegin(), entities.cend()); + return *this; } /** - * @brief Restores entities that were destroyed during serialization. - * - * This function restores the entities that were destroyed during - * serialization and gives them the versions they originally had. - * + * @brief Deprecated function. Currently, it does nothing. * @tparam Archive Type of input archive. - * @param archive A valid reference to an input archive. * @return A valid loader to continue restoring data. */ template - const basic_snapshot_loader & destroyed(Archive &archive) const { - static constexpr auto discard = true; - assure(archive, discard); - return *this; - } + [[deprecated("use ::entities instead, it imports now also destroyed entities")]] + const basic_snapshot_loader & destroyed(Archive &) const { return *this; } /** * @brief Restores components and assigns them to the right entities. @@ -306,8 +292,7 @@ public: } private: - basic_registry *reg; - force_fn_type *force; + basic_registry *reg; }; @@ -364,12 +349,12 @@ class basic_continuous_loader { using first_type = std::remove_const_t::first_type>; using second_type = typename std::decay_t::second_type; - if constexpr(std::is_same_v && std::is_same_v) { + if constexpr(std::is_same_v && std::is_same_v) { other.emplace(map(pair.first), map(pair.second)); - } else if constexpr(std::is_same_v) { + } else if constexpr(std::is_same_v) { other.emplace(map(pair.first), std::move(pair.second)); } else { - static_assert(std::is_same_v); + static_assert(std::is_same_v); other.emplace(std::move(pair.first), map(pair.second)); } } @@ -381,7 +366,7 @@ class basic_continuous_loader { auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) { // vector like container - static_assert(std::is_same_v); + static_assert(std::is_same_v); for(auto &&entt: container) { entt = map(entt); @@ -392,7 +377,7 @@ class basic_continuous_loader { void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type:: *member) { if constexpr(!std::is_same_v) { return; - } else if constexpr(std::is_same_v) { + } else if constexpr(std::is_same_v) { instance.*member = map(instance.*member); } else { // maybe a container? let's try... @@ -400,18 +385,6 @@ class basic_continuous_loader { } } - template - void assure(Archive &archive, void(basic_continuous_loader:: *member)(Entity)) { - typename traits_type::entity_type length{}; - archive(length); - - while(length--) { - Entity entt{}; - archive(entt); - (this->*member)(entt); - } - } - template void remove_if_exists() { for(auto &&ref: remloc) { @@ -429,7 +402,7 @@ class basic_continuous_loader { archive(length); while(length--) { - Entity entt{}; + entity_type entt{}; if constexpr(std::is_empty_v) { archive(entt); @@ -450,7 +423,7 @@ public: using entity_type = Entity; /** - * @brief Constructs a loader that is bound to a given registry. + * @brief Constructs an instance that is bound to a given registry. * @param source A valid reference to a registry. */ basic_continuous_loader(basic_registry &source) ENTT_NOEXCEPT @@ -475,25 +448,32 @@ public: */ template basic_continuous_loader & entities(Archive &archive) { - assure(archive, &basic_continuous_loader::restore); + typename traits_type::entity_type length{}; + entity_type entt{}; + + archive(length); + + for(decltype(length) pos{}; pos < length; ++pos) { + archive(entt); + + if(const auto entity = (to_integral(entt) & traits_type::entity_mask); entity == pos) { + restore(entt); + } else { + destroy(entt); + } + } + return *this; } /** - * @brief Restores entities that were destroyed during serialization. - * - * This function restores the entities that were destroyed during - * serialization and creates local counterparts for them if required. - * + * @brief Deprecated function. Currently, it does nothing. * @tparam Archive Type of input archive. - * @param archive A valid reference to an input archive. * @return A non-const reference to this loader. */ template - basic_continuous_loader & destroyed(Archive &archive) { - assure(archive, &basic_continuous_loader::destroy); - return *this; - } + [[deprecated("use ::entities instead, it imports now also destroyed entities")]] + basic_continuous_loader & destroyed(Archive &) { return *this; } /** * @brief Restores components and assigns them to the right entities.