Просмотр исходного кода

config:
* removed entt::page_size
* renamed ENTT_PAGE_SIZE to ENTT_SPARSE_PAGE
* added ENTT_PACKED_PAGE (page size for component arrays)

Michele Caini 4 лет назад
Родитель
Сommit
755699c31b

+ 14 - 4
docs/md/config.md

@@ -10,7 +10,8 @@
   * [ENTT_NOEXCEPT](#entt_noexcept)
   * [ENTT_USE_ATOMIC](#entt_use_atomic)
   * [ENTT_ID_TYPE](#entt_id_type)
-  * [ENTT_PAGE_SIZE](#entt_page_size)
+  * [ENTT_SPARSE_PAGE](#entt_sparse_page)
+  * [ENTT_PACKED_PAGE](#entt_packed_page)
   * [ENTT_ASSERT](#entt_assert)
     * [ENTT_DISABLE_ASSERT](#entt_disable_assert)
   * [ENTT_NO_ETO](#entt_no_eto)
@@ -59,14 +60,23 @@ the library.<br/>
 By default, its type is `std::uint32_t`. However, users can define a different
 default type if necessary.
 
-## ENTT_PAGE_SIZE
+## ENTT_SPARSE_PAGE
 
-As is known, the ECS module of `EnTT` is based on _sparse sets_. What is less
-known perhaps is that these are paged to reduce memory consumption.<br/>
+It's known that the ECS module of `EnTT` is based on _sparse sets_. What is less
+known perhaps is that the sparse arrays are paged to reduce memory usage.<br/>
 Default size of pages (that is, the number of elements they contain) is 4096 but
 users can adjust it if appropriate. In all case, the chosen value **must** be a
 power of 2.
 
+## ENTT_PACKED_PAGE
+
+Similar to sparse arrays, packed arrays of components are paginated as well. In
+However, int this case the aim isn't to reduce memory usage but to have pointer
+stability upon component creation.<br/>
+Default size of pages (that is, the number of elements they contain) is 1024 but
+users can adjust it if appropriate. In all case, the chosen value **must** be a
+power of 2.
+
 ## ENTT_ASSERT
 
 For performance reasons, `EnTT` doesn't use exceptions or any other control

+ 10 - 3
src/entt/config/config.h

@@ -29,10 +29,17 @@
 #endif
 
 
-#ifdef ENTT_PAGE_SIZE
-static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0), "ENTT_PAGE_SIZE must be a power of two");
+#ifdef ENTT_SPARSE_PAGE
+	static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE & (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two");
 #else
-#   define ENTT_PAGE_SIZE 4096
+#   define ENTT_SPARSE_PAGE 4096
+#endif
+
+
+#ifdef ENTT_PACKED_PAGE
+static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE & (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two");
+#else
+#   define ENTT_PACKED_PAGE 1024
 #endif
 
 

+ 0 - 3
src/entt/core/fwd.hpp

@@ -17,9 +17,6 @@ class basic_any;
 using id_type = ENTT_ID_TYPE;
 
 
-/*! @brief Default page size. */
-inline static constexpr auto page_size = ENTT_PAGE_SIZE;
-
 /*! @brief Alias declaration for the most common use case. */
 using any = basic_any<>;
 

+ 2 - 2
src/entt/entity/helper.hpp

@@ -152,8 +152,8 @@ Entity to_entity(const basic_registry<Entity> &reg, const Component &instance) {
     const auto view = reg.template view<const Component>();
     const auto *addr = std::addressof(instance);
 
-    for(auto it = view.rbegin(), last = view.rend(); it < last; it += page_size) {
-        if(const auto dist = (addr - std::addressof(view.template get<const Component>(*it))); dist >= 0 && dist < page_size) {
+    for(auto it = view.rbegin(), last = view.rend(); it < last; it += ENTT_PACKED_PAGE) {
+        if(const auto dist = (addr - std::addressof(view.template get<const Component>(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) {
             return *(it + dist);
         }
     }

+ 8 - 7
src/entt/entity/sparse_set.hpp

@@ -43,6 +43,7 @@ namespace entt {
 template<typename Entity, typename Allocator>
 class basic_sparse_set {
     static constexpr auto growth_factor = 1.5;
+    static constexpr auto sparse_page = ENTT_SPARSE_PAGE;
 
     using traits_type = entt_traits<Entity>;
 
@@ -160,11 +161,11 @@ class basic_sparse_set {
     };
 
     [[nodiscard]] static auto page(const Entity entt) ENTT_NOEXCEPT {
-        return size_type{(to_integral(entt) & traits_type::entity_mask) / page_size};
+        return size_type{(to_integral(entt) & traits_type::entity_mask) / sparse_page};
     }
 
     [[nodiscard]] static auto offset(const Entity entt) ENTT_NOEXCEPT {
-        return size_type{to_integral(entt) & (page_size - 1)};
+        return size_type{to_integral(entt) & (sparse_page - 1)};
     }
 
     [[nodiscard]] auto assure_page(const std::size_t idx) {
@@ -182,8 +183,8 @@ class basic_sparse_set {
         }
 
         if(!sparse[idx]) {
-            sparse[idx] = alloc_traits::allocate(allocator, page_size);
-            std::uninitialized_fill(sparse[idx], sparse[idx] + page_size, null);
+            sparse[idx] = alloc_traits::allocate(allocator, sparse_page);
+            std::uninitialized_fill(sparse[idx], sparse[idx] + sparse_page, null);
         }
 
         return sparse[idx];
@@ -210,8 +211,8 @@ class basic_sparse_set {
 
             for(size_type pos{}; pos < bucket; ++pos) {
                 if(sparse[pos]) {
-                    std::destroy(sparse[pos], sparse[pos] + page_size);
-                    alloc_traits::deallocate(allocator, sparse[pos], page_size);
+                    std::destroy(sparse[pos], sparse[pos] + sparse_page);
+                    alloc_traits::deallocate(allocator, sparse[pos], sparse_page);
                 }
 
                 bucket_alloc_traits::destroy(bucket_allocator, std::addressof(sparse[pos]));
@@ -376,7 +377,7 @@ public:
      * @return Extent of the sparse set.
      */
     [[nodiscard]] size_type extent() const ENTT_NOEXCEPT {
-        return bucket * page_size;
+        return bucket * sparse_page;
     }
 
     /**

+ 12 - 10
src/entt/entity/storage.hpp

@@ -51,6 +51,8 @@ template<typename Entity, typename Type, typename Allocator, typename>
 class basic_storage: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
     static_assert(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>, "The managed type must be at least move constructible and assignable");
 
+    static constexpr auto packed_page = ENTT_PACKED_PAGE;
+
     using underlying_type = basic_sparse_set<Entity>;
     using traits_type = entt_traits<Entity>;
 
@@ -172,23 +174,23 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
     };
 
     [[nodiscard]] static auto page(const std::size_t pos) ENTT_NOEXCEPT {
-        return pos / page_size;
+        return pos / packed_page;
     }
 
     [[nodiscard]] static auto offset(const std::size_t pos) ENTT_NOEXCEPT {
-        return pos & (page_size - 1);
+        return pos & (packed_page - 1);
     }
 
     void release_memory() {
         if(packed) {
             for(size_type pos{}; pos < bucket; ++pos) {
                 if(count) {
-                    const auto sz = count > page_size ? page_size : count;
+                    const auto sz = count > packed_page ? packed_page : count;
                     std::destroy(packed[pos], packed[pos] + sz);
                     count -= sz;
                 }
 
-                alloc_traits::deallocate(allocator, packed[pos], page_size);
+                alloc_traits::deallocate(allocator, packed[pos], packed_page);
                 bucket_alloc_traits::destroy(bucket_allocator, std::addressof(packed[pos]));
             }
 
@@ -199,7 +201,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
     void maybe_resize_packed(const std::size_t req) {
         ENTT_ASSERT(!(req < count), "Invalid request");
 
-        if(const auto length = std::exchange(bucket, (req + page_size - 1u) / page_size); bucket != length) {
+        if(const auto length = std::exchange(bucket, (req + packed_page - 1u) / packed_page); bucket != length) {
             const auto old = std::exchange(packed, bucket_alloc_traits::allocate(bucket_allocator, bucket));
 
             if(bucket > length) {
@@ -209,7 +211,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
                 }
 
                 for(auto pos = length; pos < bucket; ++pos) {
-                    bucket_alloc_traits::construct(bucket_allocator, std::addressof(packed[pos]), alloc_traits::allocate(allocator, page_size));
+                    bucket_alloc_traits::construct(bucket_allocator, std::addressof(packed[pos]), alloc_traits::allocate(allocator, packed_page));
                 }
             } else if(bucket < length) {
                 for(size_type pos{}; pos < bucket; ++pos) {
@@ -218,7 +220,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
                 }
 
                 for(auto pos = bucket; pos < length; ++pos) {
-                    alloc_traits::deallocate(allocator, old[pos], page_size);
+                    alloc_traits::deallocate(allocator, old[pos], packed_page);
                     bucket_alloc_traits::destroy(bucket_allocator, std::addressof(old[pos]));
                 }
             }
@@ -229,7 +231,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
 
     template<typename... Args>
     Type & push_back(Args &&... args) {
-        ENTT_ASSERT(count < (bucket * page_size), "No more space left");
+        ENTT_ASSERT(count < (bucket * packed_page), "No more space left");
         auto &ref = packed[page(count)][offset(count)];
 
         if constexpr(std::is_aggregate_v<value_type>) {
@@ -340,7 +342,7 @@ public:
     void reserve(const size_type cap) {
         underlying_type::reserve(cap);
 
-        if(cap > (bucket * page_size)) {
+        if(cap > (bucket * packed_page)) {
             maybe_resize_packed(cap);
         }
     }
@@ -351,7 +353,7 @@ public:
      * @return Capacity of the storage.
      */
     [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
-        return bucket * page_size;
+        return bucket * packed_page;
     }
 
     /*! @brief Requests the removal of unused capacity. */

+ 4 - 4
test/entt/entity/helper.cpp

@@ -50,7 +50,7 @@ TEST(Helper, ToEntity) {
     const auto entity = registry.create();
     registry.emplace<int>(entity);
 
-    while(registry.size<int>() < (entt::page_size - 1u)) {
+    while(registry.size<int>() < (ENTT_PACKED_PAGE - 1u)) {
         registry.emplace<int>(registry.create(), value);
     }
 
@@ -64,15 +64,15 @@ TEST(Helper, ToEntity) {
     ASSERT_EQ(entt::to_entity(registry, registry.get<int>(other)), other);
     ASSERT_EQ(entt::to_entity(registry, registry.get<int>(next)), next);
 
-    ASSERT_EQ(&registry.get<int>(entity) + entt::page_size - 1u, &registry.get<int>(other));
-    ASSERT_NE(&registry.get<int>(entity) + entt::page_size, &registry.get<int>(next));
+    ASSERT_EQ(&registry.get<int>(entity) + ENTT_PACKED_PAGE - 1u, &registry.get<int>(other));
+    ASSERT_NE(&registry.get<int>(entity) + ENTT_PACKED_PAGE, &registry.get<int>(next));
 
     registry.destroy(other);
 
     ASSERT_EQ(entt::to_entity(registry, registry.get<int>(entity)), entity);
     ASSERT_EQ(entt::to_entity(registry, registry.get<int>(next)), next);
 
-    ASSERT_EQ(&registry.get<int>(entity) + entt::page_size - 1u, &registry.get<int>(next));
+    ASSERT_EQ(&registry.get<int>(entity) + ENTT_PACKED_PAGE - 1u, &registry.get<int>(next));
 
     ASSERT_EQ(entt::to_entity(registry, 42), null);
     ASSERT_EQ(entt::to_entity(registry, value), null);

+ 4 - 4
test/entt/entity/registry.cpp

@@ -154,8 +154,8 @@ TEST(Registry, Functionalities) {
     ASSERT_TRUE(registry.empty());
 
     ASSERT_EQ(registry.capacity(), 42u);
-    ASSERT_EQ(registry.capacity<int>(), entt::page_size);
-    ASSERT_EQ(registry.capacity<char>(), entt::page_size);
+    ASSERT_EQ(registry.capacity<int>(), ENTT_PACKED_PAGE);
+    ASSERT_EQ(registry.capacity<char>(), ENTT_PACKED_PAGE);
     ASSERT_EQ(registry.size<int>(), 0u);
     ASSERT_EQ(registry.size<char>(), 0u);
     ASSERT_TRUE((registry.empty<int, char>()));
@@ -295,8 +295,8 @@ TEST(Registry, Functionalities) {
     ASSERT_EQ(registry.size<char>(), 0u);
     ASSERT_TRUE(registry.empty<int>());
 
-    ASSERT_EQ(registry.capacity<int>(), entt::page_size);
-    ASSERT_EQ(registry.capacity<char>(), entt::page_size);
+    ASSERT_EQ(registry.capacity<int>(), ENTT_PACKED_PAGE);
+    ASSERT_EQ(registry.capacity<char>(), ENTT_PACKED_PAGE);
 
     registry.shrink_to_fit<int, char>();
 

+ 17 - 17
test/entt/entity/sparse_set.cpp

@@ -96,34 +96,34 @@ TEST(SparseSet, Pagination) {
 
     ASSERT_EQ(set.extent(), 0u);
 
-    set.emplace(entt::entity{entt::page_size-1u});
+    set.emplace(entt::entity{ENTT_SPARSE_PAGE-1u});
 
-    ASSERT_EQ(set.extent(), entt::page_size);
-    ASSERT_TRUE(set.contains(entt::entity{entt::page_size-1u}));
+    ASSERT_EQ(set.extent(), ENTT_SPARSE_PAGE);
+    ASSERT_TRUE(set.contains(entt::entity{ENTT_SPARSE_PAGE-1u}));
 
-    set.emplace(entt::entity{entt::page_size});
+    set.emplace(entt::entity{ENTT_SPARSE_PAGE});
 
-    ASSERT_EQ(set.extent(), 2 * entt::page_size);
-    ASSERT_TRUE(set.contains(entt::entity{entt::page_size-1u}));
-    ASSERT_TRUE(set.contains(entt::entity{entt::page_size}));
-    ASSERT_FALSE(set.contains(entt::entity{entt::page_size+1u}));
+    ASSERT_EQ(set.extent(), 2 * ENTT_SPARSE_PAGE);
+    ASSERT_TRUE(set.contains(entt::entity{ENTT_SPARSE_PAGE-1u}));
+    ASSERT_TRUE(set.contains(entt::entity{ENTT_SPARSE_PAGE}));
+    ASSERT_FALSE(set.contains(entt::entity{ENTT_SPARSE_PAGE+1u}));
 
-    set.erase(entt::entity{entt::page_size-1u});
+    set.erase(entt::entity{ENTT_SPARSE_PAGE-1u});
 
-    ASSERT_EQ(set.extent(), 2 * entt::page_size);
-    ASSERT_FALSE(set.contains(entt::entity{entt::page_size-1u}));
-    ASSERT_TRUE(set.contains(entt::entity{entt::page_size}));
+    ASSERT_EQ(set.extent(), 2 * ENTT_SPARSE_PAGE);
+    ASSERT_FALSE(set.contains(entt::entity{ENTT_SPARSE_PAGE-1u}));
+    ASSERT_TRUE(set.contains(entt::entity{ENTT_SPARSE_PAGE}));
 
     set.shrink_to_fit();
-    set.erase(entt::entity{entt::page_size});
+    set.erase(entt::entity{ENTT_SPARSE_PAGE});
 
-    ASSERT_EQ(set.extent(), 2 * entt::page_size);
-    ASSERT_FALSE(set.contains(entt::entity{entt::page_size-1u}));
-    ASSERT_FALSE(set.contains(entt::entity{entt::page_size}));
+    ASSERT_EQ(set.extent(), 2 * ENTT_SPARSE_PAGE);
+    ASSERT_FALSE(set.contains(entt::entity{ENTT_SPARSE_PAGE-1u}));
+    ASSERT_FALSE(set.contains(entt::entity{ENTT_SPARSE_PAGE}));
 
     set.shrink_to_fit();
 
-    ASSERT_EQ(set.extent(), 2 * entt::page_size);
+    ASSERT_EQ(set.extent(), 2 * ENTT_SPARSE_PAGE);
 }
 
 TEST(SparseSet, Insert) {

+ 14 - 14
test/entt/entity/storage.cpp

@@ -40,7 +40,7 @@ TEST(Storage, Functionalities) {
 
     pool.reserve(42);
 
-    ASSERT_EQ(pool.capacity(), entt::page_size);
+    ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
     ASSERT_TRUE(pool.empty());
     ASSERT_EQ(pool.size(), 0u);
     ASSERT_EQ(std::as_const(pool).begin(), std::as_const(pool).end());
@@ -50,7 +50,7 @@ TEST(Storage, Functionalities) {
 
     pool.reserve(0);
 
-    ASSERT_EQ(pool.capacity(), entt::page_size);
+    ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
     ASSERT_TRUE(pool.empty());
 
     pool.emplace(entt::entity{41}, 3);
@@ -85,7 +85,7 @@ TEST(Storage, Functionalities) {
     ASSERT_FALSE(pool.contains(entt::entity{0}));
     ASSERT_FALSE(pool.contains(entt::entity{41}));
 
-    ASSERT_EQ(pool.capacity(), entt::page_size);
+    ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
 
     pool.shrink_to_fit();
 
@@ -223,24 +223,24 @@ TEST(Storage, Remove) {
 TEST(Storage, ShrinkToFit) {
     entt::storage<int> pool;
 
-    for(std::size_t next{}; next < entt::page_size; ++next) {
+    for(std::size_t next{}; next < ENTT_PACKED_PAGE; ++next) {
         pool.emplace(entt::entity(next));
     }
 
-    pool.emplace(entt::entity{entt::page_size});
-    pool.erase(entt::entity{entt::page_size});
+    pool.emplace(entt::entity{ENTT_PACKED_PAGE});
+    pool.erase(entt::entity{ENTT_PACKED_PAGE});
 
-    ASSERT_EQ(pool.capacity(), 2 * entt::page_size);
-    ASSERT_EQ(pool.size(), entt::page_size);
+    ASSERT_EQ(pool.capacity(), 2 * ENTT_PACKED_PAGE);
+    ASSERT_EQ(pool.size(), ENTT_PACKED_PAGE);
 
     pool.shrink_to_fit();
 
-    ASSERT_EQ(pool.capacity(), entt::page_size);
-    ASSERT_EQ(pool.size(), entt::page_size);
+    ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
+    ASSERT_EQ(pool.size(), ENTT_PACKED_PAGE);
 
     pool.clear();
 
-    ASSERT_EQ(pool.capacity(), entt::page_size);
+    ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
     ASSERT_EQ(pool.size(), 0u);
 
     pool.shrink_to_fit();
@@ -691,12 +691,12 @@ TEST(Storage, CanModifyDuringIteration) {
     entt::storage<int> pool;
     pool.emplace(entt::entity{0}, 42);
 
-    ASSERT_EQ(pool.capacity(), entt::page_size);
+    ASSERT_EQ(pool.capacity(), ENTT_PACKED_PAGE);
 
     const auto it = pool.cbegin();
-    pool.reserve(entt::page_size + 1u);
+    pool.reserve(ENTT_PACKED_PAGE + 1u);
 
-    ASSERT_EQ(pool.capacity(), 2 * entt::page_size);
+    ASSERT_EQ(pool.capacity(), 2 * ENTT_PACKED_PAGE);
 
     // this should crash with asan enabled if we break the constraint
     const auto entity = *it;