Browse Source

sparse_set/storage: prepare them for a faster and opaque ::insert

Michele Caini 4 years ago
parent
commit
22628c9ac4
2 changed files with 48 additions and 58 deletions
  1. 30 38
      src/entt/entity/sparse_set.hpp
  2. 18 20
      src/entt/entity/storage.hpp

+ 30 - 38
src/entt/entity/sparse_set.hpp

@@ -180,7 +180,7 @@ class basic_sparse_set {
     using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
     using packed_container_type = std::vector<Entity, alloc>;
 
-    [[nodiscard]] auto sparse_ptr(const Entity entt) const {
+    [[nodiscard]] auto *sparse_ptr(const Entity entt) const {
         const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
         const auto page = pos / entity_traits::page_size;
         return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr;
@@ -192,6 +192,25 @@ class basic_sparse_set {
         return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)];
     }
 
+    [[nodiscard]] auto &assure_at_least(const Entity entt) {
+        const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
+        const auto page = pos / entity_traits::page_size;
+
+        if(!(page < sparse.size())) {
+            sparse.resize(page + 1u, nullptr);
+        }
+
+        if(!sparse[page]) {
+            auto page_allocator{packed.get_allocator()};
+            sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size);
+            std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null);
+        }
+
+        auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)];
+        ENTT_ASSERT(entity_traits::to_version(elem) == entity_traits::to_version(tombstone), "Slot not available");
+        return elem;
+    }
+
     void release_sparse_pages() {
         auto page_allocator{packed.get_allocator()};
 
@@ -221,18 +240,13 @@ protected:
 
     /**
      * @brief Erases an entity from a sparse set.
-     * @param entt A valid identifier.
+     * @param pos A valid position of an element within a sparse set.
      */
-    virtual void swap_and_pop(const Entity entt) {
-        auto &ref = sparse_ref(entt);
-        const auto pos = static_cast<size_type>(entity_traits::to_entity(ref));
-        ENTT_ASSERT(packed[pos] == entt, "Invalid identifier");
-
-        packed[pos] = packed.back();
-        auto &elem = sparse_ref(packed.back());
-        elem = entity_traits::combine(entity_traits::to_integral(ref), entity_traits::to_integral(elem));
+    virtual void swap_and_pop(const std::size_t pos) {
+        sparse_ref(packed.back()) = entity_traits::combine(static_cast<typename entity_traits::entity_type>(pos), entity_traits::to_integral(packed.back()));
         // lazy self-assignment guard
-        ref = null;
+        sparse_ref(packed[pos]) = null;
+        packed[pos] = packed.back();
         // unnecessary but it helps to detect nasty bugs
         ENTT_ASSERT((packed.back() = tombstone, true), "");
         packed.pop_back();
@@ -240,16 +254,11 @@ protected:
 
     /**
      * @brief Erases an entity from a sparse set.
-     * @param entt A valid identifier.
+     * @param pos A valid position of an element within a sparse set.
      */
-    virtual void in_place_pop(const Entity entt) {
-        auto &ref = sparse_ref(entt);
-        const auto pos = static_cast<size_type>(entity_traits::to_entity(ref));
-        ENTT_ASSERT(packed[pos] == entt, "Invalid identifier");
-
+    virtual void in_place_pop(const std::size_t pos) {
+        sparse_ref(packed[pos]) = null;
         packed[pos] = std::exchange(free_list, entity_traits::combine(static_cast<typename entity_traits::entity_type>(pos), entity_traits::reserved));
-        // lazy self-assignment guard
-        ref = null;
     }
 
     /**
@@ -257,23 +266,7 @@ protected:
      * @param entt A valid identifier.
      */
     virtual void try_emplace(const Entity entt, const void * = nullptr) {
-        const auto pos = static_cast<size_type>(entity_traits::to_entity(entt));
-        const auto page = pos / entity_traits::page_size;
-
-        if(!(page < sparse.size())) {
-            sparse.resize(page + 1u, nullptr);
-        }
-
-        if(!sparse[page]) {
-            auto page_allocator{packed.get_allocator()};
-            sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size);
-            std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null);
-        }
-
-        auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)];
-        ENTT_ASSERT(entity_traits::to_version(elem) == entity_traits::to_version(tombstone), "Slot not available");
-
-        if(free_list == null) {
+        if(auto &elem = assure_at_least(entt); free_list == null) {
             packed.push_back(entt);
             elem = entity_traits::combine(static_cast<typename entity_traits::entity_type>(packed.size() - 1u), entity_traits::to_integral(entt));
         } else {
@@ -719,8 +712,7 @@ public:
      * @param entt A valid identifier.
      */
     void erase(const entity_type entt) {
-        ENTT_ASSERT(contains(entt), "Set does not contain entity");
-        (mode == deletion_policy::in_place) ? in_place_pop(entt) : swap_and_pop(entt);
+        (mode == deletion_policy::in_place) ? in_place_pop(index(entt)) : swap_and_pop(index(entt));
         ENTT_ASSERT(!contains(entt), "Destruction did not take place");
     }
 

+ 18 - 20
src/entt/entity/storage.hpp

@@ -247,7 +247,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
         return packed.first()[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)];
     }
 
-    auto assure_at_least(const std::size_t pos) {
+    auto *assure_at_least(const std::size_t pos) {
         auto &&container = packed.first();
         const auto idx = pos / comp_traits::page_size;
 
@@ -345,29 +345,25 @@ protected:
 
     /**
      * @brief Erases an element from a storage.
-     * @param entt A valid identifier.
+     * @param pos A valid position of an element within a storage.
      */
-    void swap_and_pop(const Entity entt) override {
-        auto &target = element_at(base_type::index(entt));
-        auto &last = element_at(base_type::size() - 1u);
-
+    void swap_and_pop(const std::size_t pos) override {
+        auto &elem = element_at(base_type::size() - 1u);
         // support for nosy destructors
-        [[maybe_unused]] auto unused = std::move(target);
-        target = std::move(last);
-        std::destroy_at(std::addressof(last));
-
-        base_type::swap_and_pop(entt);
+        [[maybe_unused]] auto unused = std::exchange(element_at(pos), std::move(elem));
+        base_type::swap_and_pop(pos);
+        std::destroy_at(std::addressof(elem));
     }
 
     /**
      * @brief Erases an element from a storage.
-     * @param entt A valid identifier.
+     * @param pos A valid position of an element within a storage.
      */
-    void in_place_pop(const Entity entt) override {
-        const auto pos = base_type::index(entt);
-        base_type::in_place_pop(entt);
+    void in_place_pop(const std::size_t pos) override {
+        auto &elem = element_at(pos);
+        base_type::in_place_pop(pos);
         // support for nosy destructors
-        std::destroy_at(std::addressof(element_at(pos)));
+        std::destroy_at(std::addressof(elem));
     }
 
     /**
@@ -933,17 +929,19 @@ public:
 template<typename Type>
 class sigh_storage_mixin final: public Type {
     /*! @copydoc basic_sparse_set::swap_and_pop */
-    void swap_and_pop(const typename Type::entity_type entt) final {
+    void swap_and_pop(const std::size_t pos) final {
         ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+        const auto entt = Type::operator[](pos);
         destruction.publish(*owner, entt);
-        Type::swap_and_pop(entt);
+        Type::swap_and_pop(Type::index(entt));
     }
 
     /*! @copydoc basic_sparse_set::in_place_pop */
-    void in_place_pop(const typename Type::entity_type entt) final {
+    void in_place_pop(const std::size_t pos) final {
         ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
+        const auto entt = Type::operator[](pos);
         destruction.publish(*owner, entt);
-        Type::in_place_pop(entt);
+        Type::in_place_pop(Type::index(entt));
     }
 
     /*! @copydoc basic_sparse_set::try_emplace */