From 2f781906b54cf48dfe995a0943f6d31bfcd38d37 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Sat, 11 Nov 2017 23:40:50 +0100 Subject: [PATCH] updated entity module --- src/entt/entity/registry.hpp | 124 +++++++++--------- src/entt/entity/sparse_set.hpp | 87 +++++++------ src/entt/entity/traits.hpp | 9 +- src/entt/entity/view.hpp | 13 +- test/entt/entity/benchmark.cpp | 40 +++--- test/entt/entity/sparse_set.cpp | 217 ++++++++++++++++++++++---------- 6 files changed, 299 insertions(+), 191 deletions(-) diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index c8ab53b42..ef872002b 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -102,6 +102,35 @@ class Registry { return pool(); } + template + SparseSet & handler() { + static_assert(sizeof...(Component) > 1, "!"); + const auto vtype = view_family::type(); + + if(!(vtype < handlers.size())) { + handlers.resize(vtype + 1); + } + + if(!handlers[vtype]) { + using accumulator_type = int[]; + + auto set = std::make_unique>(); + + for(auto entity: view()) { + set->construct(entity); + } + + accumulator_type accumulator = { + (ensure().append(*set, &Registry::has), 0)... + }; + + handlers[vtype] = std::move(set); + (void)accumulator; + } + + return *handlers[vtype]; + } + public: /*! @brief Underlying entity identifier. */ using entity_type = typename traits_type::entity_type; @@ -110,20 +139,18 @@ public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; - /*! @brief Default constructor, explicit on purpose. */ - explicit Registry() = default; - /*! @brief Default destructor. */ - ~Registry() = default; + /*! @brief Default constructor. */ + Registry() = default; /*! @brief Copying a registry isn't allowed. */ Registry(const Registry &) = delete; - /*! @brief Moving a registry isn't allowed. */ - Registry(Registry &&) = delete; + /*! @brief Default move constructor. */ + Registry(Registry &&) = default; /*! @brief Copying a registry isn't allowed. @return This registry. */ Registry & operator=(const Registry &) = delete; - /*! @brief Moving a registry isn't allowed. @return This registry. */ - Registry & operator=(Registry &&) = delete; + /*! @brief Default move assignment operator. @return This registry. */ + Registry & operator=(Registry &&) = default; /** * @brief Returns the number of existing components of the given type. @@ -171,7 +198,7 @@ public: } /** - * @brief Verifies if the entity identifier still refers to a valid entity. + * @brief Verifies if an entity identifier still refers to a valid entity. * @param entity An entity identifier, either valid or not. * @return True if the identifier is still valid, false otherwise. */ @@ -181,16 +208,16 @@ public: } /** - * @brief Returns the version stored along with the given entity identifier. + * @brief Returns the version stored along with an entity identifier. * @param entity An entity identifier, either valid or not. * @return Version stored along with the given entity identifier. */ version_type version(entity_type entity) const noexcept { - return version_type((entity >> traits_type::version_shift) & traits_type::version_mask); + return version_type((entity >> traits_type::entity_shift) & traits_type::version_mask); } /** - * @brief Returns the actual version for the given entity identifier. + * @brief Returns the actual version for an entity identifier. * * In case entity identifers are stored around, this function can be used to * know if they are still valid or the entity has been destroyed and @@ -209,13 +236,14 @@ public: version_type current(entity_type entity) const noexcept { const auto entt = entity & traits_type::entity_mask; assert(entt < entities.size()); - return version_type((entities[entt] >> traits_type::version_shift) & traits_type::version_mask); + return version_type((entities[entt] >> traits_type::entity_shift) & traits_type::version_mask); } /** * @brief Returns a new entity initialized with the given components. * * There are two kinds of entity identifiers: + * * * Newly created ones in case no entities have been previously destroyed. * * Recycled one with updated versions. * @@ -243,6 +271,7 @@ public: * @brief Returns a new entity to which the given components are assigned. * * There are two kinds of entity identifiers: + * * * Newly created ones in case no entities have been previously destroyed. * * Recycled one with updated versions. * @@ -269,6 +298,7 @@ public: * @brief Creates a new entity and returns it. * * There are two kinds of entity identifiers: + * * * Newly created ones in case no entities have been previously destroyed. * * Recycled one with updated versions. * @@ -287,7 +317,7 @@ public: if(available.empty()) { entity = entity_type(entities.size()); assert(entity < traits_type::entity_mask); - assert((entity >> traits_type::version_shift) == entity_type{}); + assert((entity >> traits_type::entity_shift) == entity_type{}); entities.push_back(entity); } else { entity = available.back(); @@ -316,8 +346,8 @@ public: assert(valid(entity)); const auto entt = entity & traits_type::entity_mask; - const auto version = 1 + ((entity >> traits_type::version_shift) & traits_type::version_mask); - const auto next = entt | (version << traits_type::version_shift); + const auto version = 1 + ((entity >> traits_type::entity_shift) & traits_type::version_mask); + const auto next = entt | (version << traits_type::entity_shift); entities[entt] = next; available.push_back(next); @@ -329,7 +359,7 @@ public: } /** - * @brief Assigns the given component to the given entity. + * @brief Assigns the given component to an entity. * * A new instance of the given component is created and initialized with the * arguments provided (the component must have a proper constructor or be of @@ -355,7 +385,7 @@ public: } /** - * @brief Removes the given component from the given entity. + * @brief Removes the given component from an entity. * * @warning * Attempting to use an invalid entity or to remove a component from an @@ -370,11 +400,11 @@ public: template void remove(entity_type entity) { assert(valid(entity)); - return pool().destroy(entity); + pool().destroy(entity); } /** - * @brief Checks if the given entity has all the given components. + * @brief Checks if an entity has all the given components. * * @warning * Attempting to use an invalid entity results in undefined behavior.
@@ -397,7 +427,7 @@ public: } /** - * @brief Gets a reference to the given component owned by the given entity. + * @brief Returns a reference to the given component for an entity. * * @warning * Attempting to use an invalid entity or to get a component from an entity @@ -417,7 +447,7 @@ public: } /** - * @brief Gets a reference to the given component owned by the given entity. + * @brief Returns a reference to the given component for an entity. * * @warning * Attempting to use an invalid entity or to get a component from an entity @@ -436,7 +466,7 @@ public: } /** - * @brief Replaces the given component for the given entity. + * @brief Replaces the given component for an entity. * * A new instance of the given component is created and initialized with the * arguments provided (the component must have a proper constructor or be of @@ -462,7 +492,7 @@ public: } /** - * @brief Assigns or replaces the given component to the given entity. + * @brief Assigns or replaces the given component for an entity. * * Equivalent to the following snippet (pseudocode): * @@ -499,7 +529,7 @@ public: } /** - * @brief Sorts the pool of the given component. + * @brief Sorts the pool of entities for the given component. * * The order of the elements in a pool is highly affected by assignements * of components to entities and deletions. Components are arranged to @@ -568,7 +598,7 @@ public: } /** - * @brief Resets the given component for the given entity. + * @brief Resets the given component for an entity. * * If the entity has an instance of the component, this function removes the * component from the entity. Otherwise it does nothing. @@ -616,7 +646,7 @@ public: } /** - * @brief Resets the whole registry. + * @brief Resets a whole registry. * * Destroys all the entities. After a call to `reset`, all the entities * previously created are recycled with a new version number. In case entity @@ -628,8 +658,8 @@ public: pools.clear(); for(auto &&entity: entities) { - const auto version = 1 + ((entity >> traits_type::version_shift) & traits_type::version_mask); - entity = (entity & traits_type::entity_mask) | (version << traits_type::version_shift); + const auto version = 1 + ((entity >> traits_type::entity_shift) & traits_type::version_mask); + entity = (entity & traits_type::entity_mask) | (version << traits_type::entity_shift); available.push_back(entity); } } @@ -646,6 +676,7 @@ public: * * Standard views do their best to iterate the smallest set of candidate * entites. In particular: + * * * Single component views are incredibly fast and iterate a packed array * of entities, all of which has the given component. * * Multi component views look at the number of entities available for each @@ -662,7 +693,7 @@ public: * @see View * @see PersistentView * - * @tparam Component Type of the components used to construct the view. + * @tparam Component Type of components used to construct the view. * @return A newly created standard view. */ template @@ -684,33 +715,11 @@ public: * can be prepared with this function. Just use the same set of components * that would have been used otherwise to contruct the view. * - * @tparam Component Types of the components used to prepare the view. + * @tparam Component Types of components used to prepare the view. */ template void prepare() { - static_assert(sizeof...(Component) > 1, "!"); - const auto vtype = view_family::type(); - - if(!(vtype < handlers.size())) { - handlers.resize(vtype + 1); - } - - if(!handlers[vtype]) { - using accumulator_type = int[]; - - auto handler = std::make_unique>(); - - for(auto entity: view()) { - handler->construct(entity); - } - - accumulator_type accumulator = { - (ensure().append(*handler, &Registry::has), 0)... - }; - - handlers[vtype] = std::move(handler); - (void)accumulator; - } + handler(); } /** @@ -727,6 +736,7 @@ public: * of components grows up and the most of the entities have all the given * components.
* However they have also drawbacks: + * * * Each kind of persistent view requires a dedicated data structure that * is allocated within the registry and it increases memory pressure. * * Internal data structures used to construct persistent views must be @@ -746,14 +756,12 @@ public: * @see View * @see PersistentView * - * @tparam Component Types of the components used to construct the view. + * @tparam Component Types of components used to construct the view. * @return A newly created persistent view. */ template PersistentView persistent() { - static_assert(sizeof...(Component) > 1, "!"); - prepare(); - return PersistentView{*handlers[view_family::type()], ensure()...}; + return PersistentView{handler(), ensure()...}; } private: diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp index 3fce63cb1..9100276fd 100644 --- a/src/entt/entity/sparse_set.hpp +++ b/src/entt/entity/sparse_set.hpp @@ -87,6 +87,8 @@ class SparseSet { std::size_t pos; }; + static constexpr Entity in_use = 1 << traits_type::entity_shift; + public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; @@ -97,24 +99,24 @@ public: /*! @brief Input iterator type. */ using iterator_type = Iterator; - /*! @brief Default constructor, explicit on purpose. */ - explicit SparseSet() noexcept = default; + /*! @brief Default constructor. */ + SparseSet() noexcept = default; + + /*! @brief Default destructor. */ + virtual ~SparseSet() noexcept = default; /*! @brief Copying a sparse set isn't allowed. */ SparseSet(const SparseSet &) = delete; /*! @brief Default move constructor. */ SparseSet(SparseSet &&) = default; - /*! @brief Default destructor. */ - virtual ~SparseSet() noexcept = default; - /*! @brief Copying a sparse set isn't allowed. @return This sparse set. */ SparseSet & operator=(const SparseSet &) = delete; /*! @brief Default move assignment operator. @return This sparse set. */ SparseSet & operator=(SparseSet &&) = default; /** - * @brief Returns the number of elements in the sparse set. + * @brief Returns the number of elements in a sparse set. * * The number of elements is also the size of the internal packed array. * There is no guarantee that the internal sparse array has the same size. @@ -128,8 +130,8 @@ public: } /** - * @brief Checks whether the sparse set is empty. - * @return True is the sparse set is empty, false otherwise. + * @brief Checks whether a sparse set is empty. + * @return True if the sparse set is empty, false otherwise. */ bool empty() const noexcept { return direct.empty(); @@ -188,17 +190,18 @@ public: } /** - * @brief Checks if the sparse set contains the given entity. + * @brief Checks if a sparse set contains an entity. * @param entity A valid entity identifier. * @return True if the sparse set contains the entity, false otherwise. */ bool has(entity_type entity) const noexcept { const auto entt = entity & traits_type::entity_mask; - return entt < reverse.size() && reverse[entt] < direct.size() && direct[reverse[entt]] == entity; + // the in-use control bit permits to avoid accessing the direct vector + return (entt < reverse.size()) && (reverse[entt] & in_use); } /** - * @brief Returns the position of the entity in the sparse set. + * @brief Returns the position of an entity in a sparse set. * * @warning * Attempting to get the position of an entity that doesn't belong to the @@ -211,11 +214,13 @@ public: */ pos_type get(entity_type entity) const noexcept { assert(has(entity)); - return reverse[entity & traits_type::entity_mask]; + const auto entt = entity & traits_type::entity_mask; + // we must get rid of the in-use bit for it's not part of the position + return reverse[entt] & ~in_use; } /** - * @brief Assigns an entity to the sparse set. + * @brief Assigns an entity to a sparse set. * * @warning * Attempting to assign an entity that already belongs to the sparse set @@ -224,25 +229,23 @@ public: * sparse set already contains the given entity. * * @param entity A valid entity identifier. - * @return The position of the entity in the internal packed array. */ - pos_type construct(entity_type entity) { + void construct(entity_type entity) { assert(!has(entity)); const auto entt = entity & traits_type::entity_mask; if(!(entt < reverse.size())) { - reverse.resize(entt+1); + reverse.resize(entt+1, pos_type{}); } - const auto pos = pos_type(direct.size()); - reverse[entt] = pos; + // we exploit the fact that pos_type is equal to entity_type and pos has + // traits_type::version_mask bits unused we can use to mark it as in-use + reverse[entt] = pos_type(direct.size()) | in_use; direct.emplace_back(entity); - - return pos; } /** - * @brief Removes the given entity from the sparse set. + * @brief Removes an entity from a sparse set. * * @warning * Attempting to remove an entity that doesn't belong to the sparse set @@ -256,14 +259,18 @@ public: assert(has(entity)); const auto entt = entity & traits_type::entity_mask; const auto back = direct.back() & traits_type::entity_mask; - const auto pos = reverse[entt]; - reverse[back] = pos; + const auto pos = reverse[entt] & ~in_use; + // the order matters: if back and entt are the same (for the sparse set + // has size 1), switching the two lines below doesn't work as expected + reverse[back] = pos | in_use; + reverse[entt] = pos; + // swap-and-pop the last element with the selected ont direct[pos] = direct.back(); direct.pop_back(); } /** - * @brief Swaps the position of the entities in the internal packed array. + * @brief Swaps the position of two entities in the internal packed array. * * For what it's worth, this function affects both the internal sparse array * and the internal packed array. Users should not care of that anyway. @@ -280,10 +287,11 @@ public: virtual void swap(entity_type lhs, entity_type rhs) { assert(has(lhs)); assert(has(rhs)); - const auto le = lhs & traits_type::entity_mask; - const auto re = rhs & traits_type::entity_mask; - std::swap(direct[reverse[le]], direct[reverse[re]]); - std::swap(reverse[le], reverse[re]); + auto &le = reverse[lhs & traits_type::entity_mask]; + auto &re = reverse[rhs & traits_type::entity_mask]; + // we must get rid of the in-use bit for it's not part of the position + std::swap(direct[le & ~in_use], direct[re & ~in_use]); + std::swap(le, re); } /** @@ -316,7 +324,7 @@ public: } /** - * @brief Sort entities according to their order in the given sparse set. + * @brief Sort entities according to their order in a sparse set. * * Entities that are part of both the sparse sets are ordered internally * according to the order they have in `other`. All the other entities goes @@ -363,7 +371,7 @@ public: } /** - * @brief Resets the sparse set. + * @brief Resets a sparse set. */ virtual void reset() { reverse.clear(); @@ -371,7 +379,7 @@ public: } private: - std::vector reverse; + std::vector reverse; std::vector direct; }; @@ -414,8 +422,8 @@ public: /*! @brief Input iterator type. */ using iterator_type = typename underlying_type::iterator_type; - /*! @brief Default constructor, explicit on purpose. */ - explicit SparseSet() noexcept = default; + /*! @brief Default constructor. */ + SparseSet() noexcept = default; /*! @brief Copying a sparse set isn't allowed. */ SparseSet(const SparseSet &) = delete; @@ -466,7 +474,7 @@ public: } /** - * @brief Returns the object associated to the given entity. + * @brief Returns the object associated to an entity. * * @warning * Attempting to use an entity that doesn't belong to the sparse set results @@ -482,7 +490,7 @@ public: } /** - * @brief Returns the object associated to the given entity. + * @brief Returns the object associated to an entity. * * @warning * Attempting to use an entity that doesn't belong to the sparse set results @@ -498,7 +506,7 @@ public: } /** - * @brief Assigns an entity to the sparse set and constructs its object. + * @brief Assigns an entity to a sparse set and constructs its object. * * @warning * Attempting to use an entity that already belongs to the sparse set @@ -519,7 +527,7 @@ public: } /** - * @brief Removes an entity from the sparse set and destroies its object. + * @brief Removes an entity from a sparse set and destroies its object. * * @warning * Attempting to use an entity that doesn't belong to the sparse set results @@ -530,13 +538,14 @@ public: * @param entity A valid entity identifier. */ void destroy(entity_type entity) override { + // swaps isn't required here, we are getting rid of the last element instances[underlying_type::get(entity)] = std::move(instances.back()); instances.pop_back(); underlying_type::destroy(entity); } /** - * @brief Swaps the two entities and their objects. + * @brief Swaps two entities and their objects. * * @note * This function doesn't swap objects between entities. It exchanges entity @@ -557,7 +566,7 @@ public: } /** - * @brief Resets the sparse set. + * @brief Resets a sparse set. */ void reset() override { underlying_type::reset(); diff --git a/src/entt/entity/traits.hpp b/src/entt/entity/traits.hpp index 97573a4bd..82d5b466a 100644 --- a/src/entt/entity/traits.hpp +++ b/src/entt/entity/traits.hpp @@ -22,6 +22,7 @@ struct entt_traits; * @brief Entity traits for a 16 bits entity identifier. * * A 16 bits entity identifier guarantees: + * * * 12 bits for the entity number (up to 4k entities). * * 4 bit for the version (resets in [0-15]). */ @@ -37,7 +38,7 @@ struct entt_traits { /*! @brief Mask to use to get the version out of an identifier. */ static constexpr auto version_mask = 0xF; /*! @brief Extent of the entity number within an identifier. */ - static constexpr auto version_shift = 12; + static constexpr auto entity_shift = 12; }; @@ -45,6 +46,7 @@ struct entt_traits { * @brief Entity traits for a 32 bits entity identifier. * * A 32 bits entity identifier guarantees: + * * * 24 bits for the entity number (suitable for almost all the games). * * 8 bit for the version (resets in [0-255]). */ @@ -60,7 +62,7 @@ struct entt_traits { /*! @brief Mask to use to get the version out of an identifier. */ static constexpr auto version_mask = 0xFF; /*! @brief Extent of the entity number within an identifier. */ - static constexpr auto version_shift = 24; + static constexpr auto entity_shift = 24; }; @@ -68,6 +70,7 @@ struct entt_traits { * @brief Entity traits for a 64 bits entity identifier. * * A 64 bits entity identifier guarantees: + * * * 40 bits for the entity number (an indecently large number). * * 24 bit for the version (an indecently large number). */ @@ -83,7 +86,7 @@ struct entt_traits { /*! @brief Mask to use to get the version out of an identifier. */ static constexpr auto version_mask = 0xFFFFFF; /*! @brief Extent of the entity number within an identifier. */ - static constexpr auto version_shift = 40; + static constexpr auto entity_shift = 40; }; diff --git a/src/entt/entity/view.hpp b/src/entt/entity/view.hpp index df8299516..e474128c2 100644 --- a/src/entt/entity/view.hpp +++ b/src/entt/entity/view.hpp @@ -70,6 +70,7 @@ public: * @brief Constructs a persistent view around a dedicated pool of entities. * * A persistent view is created out of: + * * * A dedicated pool of entities that is shared between all the persistent * views of the same type. * * A bunch of pools of components to which to refer to get instances. @@ -77,7 +78,7 @@ public: * @param view Shared reference to a dedicated pool of entities. * @param pools References to pools of components. */ - explicit PersistentView(view_type &view, pool_type&... pools) noexcept + PersistentView(view_type &view, pool_type&... pools) noexcept : view{view}, pools{pools...} {} @@ -240,9 +241,9 @@ public: * * @note * The shared pool of entities and thus its order is affected by the changes - * to each and every pool of components that it tracks. Therefore changes to - * the pools of components can quickly ruin the order imposed to the pool of - * entities shared between the persistent views. + * to each and every pool that it tracks. Therefore changes to those pools + * can quickly ruin the order imposed to the pool of entities shared between + * the persistent views. * * @tparam Comp Type of the component to use to impose the order. */ @@ -368,7 +369,7 @@ public: * @param pool A reference to a pool of components. * @param other Other references to pools of components. */ - explicit View(pool_type &pool, pool_type&... other) noexcept + View(pool_type &pool, pool_type&... other) noexcept : pools{pool, other...}, view{nullptr} { reset(); @@ -576,7 +577,7 @@ public: * @brief Constructs a view out of a pool of components. * @param pool A reference to a pool of components. */ - explicit View(pool_type &pool) noexcept + View(pool_type &pool) noexcept : pool{pool} {} diff --git a/test/entt/entity/benchmark.cpp b/test/entt/entity/benchmark.cpp index 0db6b3b7d..33e12e13c 100644 --- a/test/entt/entity/benchmark.cpp +++ b/test/entt/entity/benchmark.cpp @@ -30,7 +30,7 @@ private: std::chrono::time_point start; }; -TEST(DefaultRegistry, Construct) { +TEST(Benchmark, Construct) { entt::DefaultRegistry registry; std::cout << "Constructing 10000000 entities" << std::endl; @@ -44,7 +44,7 @@ TEST(DefaultRegistry, Construct) { timer.elapsed(); } -TEST(DefaultRegistry, Destroy) { +TEST(Benchmark, Destroy) { entt::DefaultRegistry registry; std::vector entities{}; @@ -63,7 +63,7 @@ TEST(DefaultRegistry, Destroy) { timer.elapsed(); } -TEST(DefaultRegistry, IterateCreateDeleteSingleComponent) { +TEST(Benchmark, IterateCreateDeleteSingleComponent) { entt::DefaultRegistry registry; std::cout << "Looping 10000 times creating and deleting a random number of entities" << std::endl; @@ -87,7 +87,7 @@ TEST(DefaultRegistry, IterateCreateDeleteSingleComponent) { timer.elapsed(); } -TEST(DefaultRegistry, IterateSingleComponent10M) { +TEST(Benchmark, IterateSingleComponent10M) { entt::DefaultRegistry registry; std::cout << "Iterating over 10000000 entities, one component" << std::endl; @@ -101,7 +101,7 @@ TEST(DefaultRegistry, IterateSingleComponent10M) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTwoComponents10M) { +TEST(Benchmark, IterateTwoComponents10M) { entt::DefaultRegistry registry; std::cout << "Iterating over 10000000 entities, two components" << std::endl; @@ -115,7 +115,7 @@ TEST(DefaultRegistry, IterateTwoComponents10M) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTwoComponents10MHalf) { +TEST(Benchmark, IterateTwoComponents10MHalf) { entt::DefaultRegistry registry; std::cout << "Iterating over 10000000 entities, two components, half of the entities have all the components" << std::endl; @@ -130,7 +130,7 @@ TEST(DefaultRegistry, IterateTwoComponents10MHalf) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTwoComponents10MOne) { +TEST(Benchmark, IterateTwoComponents10MOne) { entt::DefaultRegistry registry; std::cout << "Iterating over 10000000 entities, two components, only one entity has all the components" << std::endl; @@ -145,7 +145,7 @@ TEST(DefaultRegistry, IterateTwoComponents10MOne) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTwoComponentsPersistent10M) { +TEST(Benchmark, IterateTwoComponentsPersistent10M) { entt::DefaultRegistry registry; registry.prepare(); @@ -160,7 +160,7 @@ TEST(DefaultRegistry, IterateTwoComponentsPersistent10M) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTwoComponentsPersistent10MHalf) { +TEST(Benchmark, IterateTwoComponentsPersistent10MHalf) { entt::DefaultRegistry registry; registry.prepare(); @@ -176,7 +176,7 @@ TEST(DefaultRegistry, IterateTwoComponentsPersistent10MHalf) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTwoComponentsPersistent10MOne) { +TEST(Benchmark, IterateTwoComponentsPersistent10MOne) { entt::DefaultRegistry registry; registry.prepare(); @@ -192,7 +192,7 @@ TEST(DefaultRegistry, IterateTwoComponentsPersistent10MOne) { timer.elapsed(); } -TEST(DefaultRegistry, IterateFiveComponents10M) { +TEST(Benchmark, IterateFiveComponents10M) { entt::DefaultRegistry registry; std::cout << "Iterating over 10000000 entities, five components" << std::endl; @@ -206,7 +206,7 @@ TEST(DefaultRegistry, IterateFiveComponents10M) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTenComponents10M) { +TEST(Benchmark, IterateTenComponents10M) { entt::DefaultRegistry registry; std::cout << "Iterating over 10000000 entities, ten components" << std::endl; @@ -220,7 +220,7 @@ TEST(DefaultRegistry, IterateTenComponents10M) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTenComponents10MHalf) { +TEST(Benchmark, IterateTenComponents10MHalf) { entt::DefaultRegistry registry; std::cout << "Iterating over 10000000 entities, ten components, half of the entities have all the components" << std::endl; @@ -235,7 +235,7 @@ TEST(DefaultRegistry, IterateTenComponents10MHalf) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTenComponents10MOne) { +TEST(Benchmark, IterateTenComponents10MOne) { entt::DefaultRegistry registry; std::cout << "Iterating over 10000000 entities, ten components, only one entity has all the components" << std::endl; @@ -250,7 +250,7 @@ TEST(DefaultRegistry, IterateTenComponents10MOne) { timer.elapsed(); } -TEST(DefaultRegistry, IterateFiveComponentsPersistent10M) { +TEST(Benchmark, IterateFiveComponentsPersistent10M) { entt::DefaultRegistry registry; registry.prepare, Comp<2>, Comp<3>>(); @@ -265,7 +265,7 @@ TEST(DefaultRegistry, IterateFiveComponentsPersistent10M) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTenComponentsPersistent10M) { +TEST(Benchmark, IterateTenComponentsPersistent10M) { entt::DefaultRegistry registry; registry.prepare, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); @@ -280,7 +280,7 @@ TEST(DefaultRegistry, IterateTenComponentsPersistent10M) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTenComponentsPersistent10MHalf) { +TEST(Benchmark, IterateTenComponentsPersistent10MHalf) { entt::DefaultRegistry registry; registry.prepare, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); @@ -296,7 +296,7 @@ TEST(DefaultRegistry, IterateTenComponentsPersistent10MHalf) { timer.elapsed(); } -TEST(DefaultRegistry, IterateTenComponentsPersistent10MOne) { +TEST(Benchmark, IterateTenComponentsPersistent10MOne) { entt::DefaultRegistry registry; registry.prepare, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); @@ -312,7 +312,7 @@ TEST(DefaultRegistry, IterateTenComponentsPersistent10MOne) { timer.elapsed(); } -TEST(DefaultRegistry, SortSingle) { +TEST(Benchmark, SortSingle) { entt::DefaultRegistry registry; std::vector entities{}; @@ -332,7 +332,7 @@ TEST(DefaultRegistry, SortSingle) { timer.elapsed(); } -TEST(DefaultRegistry, SortMulti) { +TEST(Benchmark, SortMulti) { entt::DefaultRegistry registry; std::vector entities{}; diff --git a/test/entt/entity/sparse_set.cpp b/test/entt/entity/sparse_set.cpp index 0b2671d1d..d68b1a588 100644 --- a/test/entt/entity/sparse_set.cpp +++ b/test/entt/entity/sparse_set.cpp @@ -10,7 +10,9 @@ TEST(SparseSetNoType, Functionalities) { ASSERT_FALSE(set.has(0)); ASSERT_FALSE(set.has(42)); - ASSERT_EQ(set.construct(42), 0u); + set.construct(42); + + ASSERT_EQ(set.get(42), 0u); ASSERT_FALSE(set.empty()); ASSERT_EQ(set.size(), 1u); @@ -27,7 +29,9 @@ TEST(SparseSetNoType, Functionalities) { ASSERT_FALSE(set.has(0)); ASSERT_FALSE(set.has(42)); - ASSERT_EQ(set.construct(42), 0u); + set.construct(42); + + ASSERT_EQ(set.get(42), 0u); set.reset(); @@ -45,9 +49,13 @@ TEST(SparseSetNoType, Functionalities) { TEST(SparseSetNoType, DataBeginEnd) { entt::SparseSet set; - ASSERT_EQ(set.construct(3), 0u); - ASSERT_EQ(set.construct(12), 1u); - ASSERT_EQ(set.construct(42), 2u); + set.construct(3); + set.construct(12); + set.construct(42); + + ASSERT_EQ(set.get(3), 0u); + ASSERT_EQ(set.get(12), 1u); + ASSERT_EQ(set.get(42), 2u); ASSERT_EQ(*(set.data() + 0u), 3u); ASSERT_EQ(*(set.data() + 1u), 12u); @@ -62,6 +70,12 @@ TEST(SparseSetNoType, DataBeginEnd) { ASSERT_EQ(begin, end); } +TEST(SparseSetWithType, AggregatesMustWork) { + struct AggregateType { int value; }; + // the goal of this test is to enforce the requirements for aggregate types + entt::SparseSet{}.construct(0, 42); +} + TEST(SparseSetWithType, Functionalities) { entt::SparseSet set; @@ -71,8 +85,9 @@ TEST(SparseSetWithType, Functionalities) { ASSERT_FALSE(set.has(0)); ASSERT_FALSE(set.has(42)); - ASSERT_EQ(set.construct(42, 3), 3); + set.construct(42, 3); + ASSERT_EQ(set.get(42), 3); ASSERT_FALSE(set.empty()); ASSERT_EQ(set.size(), 1u); ASSERT_NE(set.begin(), set.end()); @@ -88,7 +103,9 @@ TEST(SparseSetWithType, Functionalities) { ASSERT_FALSE(set.has(0)); ASSERT_FALSE(set.has(42)); - ASSERT_EQ(set.construct(42, 12), 12); + set.construct(42, 12); + + ASSERT_EQ(set.get(42), 12); set.reset(); @@ -106,9 +123,13 @@ TEST(SparseSetWithType, Functionalities) { TEST(SparseSetWithType, RawBeginEnd) { entt::SparseSet set; - ASSERT_EQ(set.construct(3, 3), 3); - ASSERT_EQ(set.construct(12, 6), 6); - ASSERT_EQ(set.construct(42, 9), 9); + set.construct(3, 3); + set.construct(12, 6); + set.construct(42, 9); + + ASSERT_EQ(set.get(3), 3); + ASSERT_EQ(set.get(12), 6); + ASSERT_EQ(set.get(42), 9); ASSERT_EQ(*(set.raw() + 0u), 3); ASSERT_EQ(*(set.raw() + 1u), 6); @@ -126,11 +147,17 @@ TEST(SparseSetWithType, RawBeginEnd) { TEST(SparseSetWithType, SortOrdered) { entt::SparseSet set; - ASSERT_EQ(set.construct(12, 12), 12); - ASSERT_EQ(set.construct(42, 9), 9); - ASSERT_EQ(set.construct(7, 6), 6); - ASSERT_EQ(set.construct(3, 3), 3); - ASSERT_EQ(set.construct(9, 1), 1); + set.construct(12, 12); + set.construct(42, 9); + set.construct(7, 6); + set.construct(3, 3); + set.construct(9, 1); + + ASSERT_EQ(set.get(12), 12); + ASSERT_EQ(set.get(42), 9); + ASSERT_EQ(set.get(7), 6); + ASSERT_EQ(set.get(3), 3); + ASSERT_EQ(set.get(9), 1); set.sort([&set](auto lhs, auto rhs) { return set.get(lhs) < set.get(rhs); @@ -156,11 +183,17 @@ TEST(SparseSetWithType, SortOrdered) { TEST(SparseSetWithType, SortReverse) { entt::SparseSet set; - ASSERT_EQ(set.construct(12, 1), 1); - ASSERT_EQ(set.construct(42, 3), 3); - ASSERT_EQ(set.construct(7, 6), 6); - ASSERT_EQ(set.construct(3, 9), 9); - ASSERT_EQ(set.construct(9, 12), 12); + set.construct(12, 1); + set.construct(42, 3); + set.construct(7, 6); + set.construct(3, 9); + set.construct(9, 12); + + ASSERT_EQ(set.get(12), 1); + ASSERT_EQ(set.get(42), 3); + ASSERT_EQ(set.get(7), 6); + ASSERT_EQ(set.get(3), 9); + ASSERT_EQ(set.get(9), 12); set.sort([&set](auto lhs, auto rhs) { return set.get(lhs) < set.get(rhs); @@ -186,11 +219,17 @@ TEST(SparseSetWithType, SortReverse) { TEST(SparseSetWithType, SortUnordered) { entt::SparseSet set; - ASSERT_EQ(set.construct(12, 6), 6); - ASSERT_EQ(set.construct(42, 3), 3); - ASSERT_EQ(set.construct(7, 1), 1); - ASSERT_EQ(set.construct(3, 9), 9); - ASSERT_EQ(set.construct(9, 12), 12); + set.construct(12, 6); + set.construct(42, 3); + set.construct(7, 1); + set.construct(3, 9); + set.construct(9, 12); + + ASSERT_EQ(set.get(12), 6); + ASSERT_EQ(set.get(42), 3); + ASSERT_EQ(set.get(7), 1); + ASSERT_EQ(set.get(3), 9); + ASSERT_EQ(set.get(9), 12); set.sort([&set](auto lhs, auto rhs) { return set.get(lhs) < set.get(rhs); @@ -218,9 +257,13 @@ TEST(SparseSetWithType, RespectDisjoint) { entt::SparseSet rhs; const auto &clhs = lhs; - ASSERT_EQ(lhs.construct(3, 3), 3); - ASSERT_EQ(lhs.construct(12, 6), 6); - ASSERT_EQ(lhs.construct(42, 9), 9); + lhs.construct(3, 3); + lhs.construct(12, 6); + lhs.construct(42, 9); + + ASSERT_EQ(lhs.get(3), 3); + ASSERT_EQ(lhs.get(12), 6); + ASSERT_EQ(lhs.get(42), 9); lhs.respect(rhs); @@ -242,10 +285,15 @@ TEST(SparseSetWithType, RespectOverlap) { entt::SparseSet rhs; const auto &clhs = lhs; - ASSERT_EQ(lhs.construct(3, 3), 3); - ASSERT_EQ(lhs.construct(12, 6), 6); - ASSERT_EQ(lhs.construct(42, 9), 9); - ASSERT_EQ(rhs.construct(12, 6), 6); + lhs.construct(3, 3); + lhs.construct(12, 6); + lhs.construct(42, 9); + rhs.construct(12, 6); + + ASSERT_EQ(lhs.get(3), 3); + ASSERT_EQ(lhs.get(12), 6); + ASSERT_EQ(lhs.get(42), 9); + ASSERT_EQ(rhs.get(12), 6); lhs.respect(rhs); @@ -266,18 +314,31 @@ TEST(SparseSetWithType, RespectOrdered) { entt::SparseSet lhs; entt::SparseSet rhs; - ASSERT_EQ(lhs.construct(1, 0), 0); - ASSERT_EQ(lhs.construct(2, 0), 0); - ASSERT_EQ(lhs.construct(3, 0), 0); - ASSERT_EQ(lhs.construct(4, 0), 0); - ASSERT_EQ(lhs.construct(5, 0), 0); + lhs.construct(1, 0); + lhs.construct(2, 0); + lhs.construct(3, 0); + lhs.construct(4, 0); + lhs.construct(5, 0); - ASSERT_EQ(rhs.construct(6, 0), 0); - ASSERT_EQ(rhs.construct(1, 0), 0); - ASSERT_EQ(rhs.construct(2, 0), 0); - ASSERT_EQ(rhs.construct(3, 0), 0); - ASSERT_EQ(rhs.construct(4, 0), 0); - ASSERT_EQ(rhs.construct(5, 0), 0); + ASSERT_EQ(lhs.get(1), 0); + ASSERT_EQ(lhs.get(2), 0); + ASSERT_EQ(lhs.get(3), 0); + ASSERT_EQ(lhs.get(4), 0); + ASSERT_EQ(lhs.get(5), 0); + + rhs.construct(6, 0); + rhs.construct(1, 0); + rhs.construct(2, 0); + rhs.construct(3, 0); + rhs.construct(4, 0); + rhs.construct(5, 0); + + ASSERT_EQ(rhs.get(6), 0); + ASSERT_EQ(rhs.get(1), 0); + ASSERT_EQ(rhs.get(2), 0); + ASSERT_EQ(rhs.get(3), 0); + ASSERT_EQ(rhs.get(4), 0); + ASSERT_EQ(rhs.get(5), 0); rhs.respect(lhs); @@ -299,18 +360,31 @@ TEST(SparseSetWithType, RespectReverse) { entt::SparseSet lhs; entt::SparseSet rhs; - ASSERT_EQ(lhs.construct(1, 0), 0); - ASSERT_EQ(lhs.construct(2, 0), 0); - ASSERT_EQ(lhs.construct(3, 0), 0); - ASSERT_EQ(lhs.construct(4, 0), 0); - ASSERT_EQ(lhs.construct(5, 0), 0); + lhs.construct(1, 0); + lhs.construct(2, 0); + lhs.construct(3, 0); + lhs.construct(4, 0); + lhs.construct(5, 0); - ASSERT_EQ(rhs.construct(5, 0), 0); - ASSERT_EQ(rhs.construct(4, 0), 0); - ASSERT_EQ(rhs.construct(3, 0), 0); - ASSERT_EQ(rhs.construct(2, 0), 0); - ASSERT_EQ(rhs.construct(1, 0), 0); - ASSERT_EQ(rhs.construct(6, 0), 0); + ASSERT_EQ(lhs.get(1), 0); + ASSERT_EQ(lhs.get(2), 0); + ASSERT_EQ(lhs.get(3), 0); + ASSERT_EQ(lhs.get(4), 0); + ASSERT_EQ(lhs.get(5), 0); + + rhs.construct(5, 0); + rhs.construct(4, 0); + rhs.construct(3, 0); + rhs.construct(2, 0); + rhs.construct(1, 0); + rhs.construct(6, 0); + + ASSERT_EQ(rhs.get(5), 0); + ASSERT_EQ(rhs.get(4), 0); + ASSERT_EQ(rhs.get(3), 0); + ASSERT_EQ(rhs.get(2), 0); + ASSERT_EQ(rhs.get(1), 0); + ASSERT_EQ(rhs.get(6), 0); rhs.respect(lhs); @@ -332,18 +406,31 @@ TEST(SparseSetWithType, RespectUnordered) { entt::SparseSet lhs; entt::SparseSet rhs; - ASSERT_EQ(lhs.construct(1, 0), 0); - ASSERT_EQ(lhs.construct(2, 0), 0); - ASSERT_EQ(lhs.construct(3, 0), 0); - ASSERT_EQ(lhs.construct(4, 0), 0); - ASSERT_EQ(lhs.construct(5, 0), 0); + lhs.construct(1, 0); + lhs.construct(2, 0); + lhs.construct(3, 0); + lhs.construct(4, 0); + lhs.construct(5, 0); - ASSERT_EQ(rhs.construct(3, 0), 0); - ASSERT_EQ(rhs.construct(2, 0), 0); - ASSERT_EQ(rhs.construct(6, 0), 0); - ASSERT_EQ(rhs.construct(1, 0), 0); - ASSERT_EQ(rhs.construct(4, 0), 0); - ASSERT_EQ(rhs.construct(5, 0), 0); + ASSERT_EQ(lhs.get(1), 0); + ASSERT_EQ(lhs.get(2), 0); + ASSERT_EQ(lhs.get(3), 0); + ASSERT_EQ(lhs.get(4), 0); + ASSERT_EQ(lhs.get(5), 0); + + rhs.construct(3, 0); + rhs.construct(2, 0); + rhs.construct(6, 0); + rhs.construct(1, 0); + rhs.construct(4, 0); + rhs.construct(5, 0); + + ASSERT_EQ(rhs.get(3), 0); + ASSERT_EQ(rhs.get(2), 0); + ASSERT_EQ(rhs.get(6), 0); + ASSERT_EQ(rhs.get(1), 0); + ASSERT_EQ(rhs.get(4), 0); + ASSERT_EQ(rhs.get(5), 0); rhs.respect(lhs);