shrink_to_fit to release pages
This commit is contained in:
1
TODO
1
TODO
@@ -27,4 +27,3 @@
|
||||
* review sparse set to allow customization (mix pack in the spec, base is position only)
|
||||
- non-owning groups can iterate pages and skip empty ones, this should mitigate the lack of the packed array
|
||||
* review 64 bit id: user defined area + dedicated member on the registry to set it
|
||||
* improve pagination: use a support integer to count elements and remove pages when empty
|
||||
|
||||
@@ -378,6 +378,15 @@ public:
|
||||
return cpool ? cpool->empty() : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Requests the removal of unused capacity for a given component.
|
||||
* @tparam Component Type of component for which to reclaim unused capacity.
|
||||
*/
|
||||
template<typename Component>
|
||||
void shrink_to_fit() {
|
||||
assure<Component>()->shrink_to_fit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if there exists at least an entity still in use.
|
||||
* @return True if at least an entity is still in use, false otherwise.
|
||||
|
||||
@@ -169,10 +169,10 @@ class sparse_set<Entity> {
|
||||
reverse.resize(page+1);
|
||||
}
|
||||
|
||||
if(!reverse[page]) {
|
||||
reverse[page] = std::make_unique<entity_type[]>(entt_per_page);
|
||||
if(!reverse[page].first) {
|
||||
reverse[page].first = std::make_unique<entity_type[]>(entt_per_page);
|
||||
// null is safe in all cases for our purposes
|
||||
std::fill_n(reverse[page].get(), entt_per_page, null);
|
||||
std::fill_n(reverse[page].first.get(), entt_per_page, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,13 +199,14 @@ public:
|
||||
* @param other The instance to copy from.
|
||||
*/
|
||||
sparse_set(const sparse_set &other)
|
||||
: reverse(),
|
||||
: reverse{},
|
||||
direct{other.direct}
|
||||
{
|
||||
for(size_type i = {}, last = other.reverse.size(); i < last; ++i) {
|
||||
if(other.reverse[i]) {
|
||||
if(other.reverse[i].first) {
|
||||
assure(i);
|
||||
std::copy_n(other.reverse[i].get(), entt_per_page, reverse[i].get());
|
||||
std::copy_n(other.reverse[i].first.get(), entt_per_page, reverse[i].first.get());
|
||||
reverse[i].second = other.reverse[i].second;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,6 +255,22 @@ public:
|
||||
return direct.capacity();
|
||||
}
|
||||
|
||||
/*! @brief Requests the removal of unused capacity. */
|
||||
virtual void shrink_to_fit() {
|
||||
while(!reverse.empty() && !reverse.back().second) {
|
||||
reverse.pop_back();
|
||||
}
|
||||
|
||||
for(auto &&data: reverse) {
|
||||
if(!data.second) {
|
||||
data.first.reset();
|
||||
}
|
||||
}
|
||||
|
||||
reverse.shrink_to_fit();
|
||||
direct.shrink_to_fit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the extent of a sparse set.
|
||||
*
|
||||
@@ -361,7 +378,7 @@ public:
|
||||
bool has(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
auto [page, offset] = index(entt);
|
||||
// testing against null permits to avoid accessing the direct vector
|
||||
return (page < reverse.size() && reverse[page] && reverse[page][offset] != null);
|
||||
return (page < reverse.size() && reverse[page].second && reverse[page].first[offset] != null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -379,7 +396,7 @@ public:
|
||||
size_type get(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(has(entt));
|
||||
auto [page, offset] = index(entt);
|
||||
return size_type(reverse[page][offset]);
|
||||
return size_type(reverse[page].first[offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,7 +414,8 @@ public:
|
||||
ENTT_ASSERT(!has(entt));
|
||||
auto [page, offset] = index(entt);
|
||||
assure(page);
|
||||
reverse[page][offset] = entity_type(direct.size());
|
||||
reverse[page].first[offset] = entity_type(direct.size());
|
||||
reverse[page].second++;
|
||||
direct.push_back(entt);
|
||||
}
|
||||
|
||||
@@ -420,7 +438,8 @@ public:
|
||||
ENTT_ASSERT(!has(entt));
|
||||
auto [page, offset] = index(entt);
|
||||
assure(page);
|
||||
reverse[page][offset] = next++;
|
||||
reverse[page].first[offset] = next++;
|
||||
reverse[page].second++;
|
||||
});
|
||||
|
||||
direct.insert(direct.end(), first, last);
|
||||
@@ -441,9 +460,10 @@ public:
|
||||
ENTT_ASSERT(has(entt));
|
||||
auto [from_page, from_offset] = index(entt);
|
||||
auto [to_page, to_offset] = index(direct.back());
|
||||
std::swap(direct[size_type(reverse[from_page][from_offset])], direct.back());
|
||||
std::swap(reverse[from_page][from_offset], reverse[to_page][to_offset]);
|
||||
reverse[from_page][from_offset] = null;
|
||||
std::swap(direct[size_type(reverse[from_page].first[from_offset])], direct.back());
|
||||
std::swap(reverse[from_page].first[from_offset], reverse[to_page].first[to_offset]);
|
||||
reverse[from_page].first[from_offset] = null;
|
||||
reverse[from_page].second--;
|
||||
direct.pop_back();
|
||||
}
|
||||
|
||||
@@ -467,7 +487,7 @@ public:
|
||||
ENTT_ASSERT(rhs < direct.size());
|
||||
auto [src_page, src_offset] = index(direct[lhs]);
|
||||
auto [dst_page, dst_offset] = index(direct[rhs]);
|
||||
std::swap(reverse[src_page][src_offset], reverse[dst_page][dst_offset]);
|
||||
std::swap(reverse[src_page].first[src_offset], reverse[dst_page].first[dst_offset]);
|
||||
std::swap(direct[lhs], direct[rhs]);
|
||||
}
|
||||
|
||||
@@ -530,7 +550,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<entity_type[]>> reverse;
|
||||
std::vector<std::pair<std::unique_ptr<entity_type[]>, size_type>> reverse;
|
||||
std::vector<entity_type> direct;
|
||||
};
|
||||
|
||||
@@ -792,6 +812,21 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Requests the removal of unused capacity.
|
||||
*
|
||||
* @note
|
||||
* Empty components aren't explicitly instantiated. Only one instance of the
|
||||
* given type is created. Therefore, this function does nothing.
|
||||
*/
|
||||
void shrink_to_fit() override {
|
||||
underlying_type::shrink_to_fit();
|
||||
|
||||
if constexpr(!std::is_empty_v<object_type>) {
|
||||
instances.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the array of objects.
|
||||
*
|
||||
|
||||
@@ -240,6 +240,15 @@ TEST(Registry, Functionalities) {
|
||||
ASSERT_EQ(registry.size<int>(), entt::registry::size_type{0});
|
||||
ASSERT_EQ(registry.size<char>(), entt::registry::size_type{0});
|
||||
ASSERT_TRUE(registry.empty<int>());
|
||||
|
||||
ASSERT_EQ(registry.capacity<int>(), entt::registry::size_type{8});
|
||||
ASSERT_EQ(registry.capacity<char>(), entt::registry::size_type{8});
|
||||
|
||||
registry.shrink_to_fit<int>();
|
||||
registry.shrink_to_fit<char>();
|
||||
|
||||
ASSERT_EQ(registry.capacity<int>(), entt::registry::size_type{});
|
||||
ASSERT_EQ(registry.capacity<char>(), entt::registry::size_type{});
|
||||
}
|
||||
|
||||
TEST(Registry, Identifiers) {
|
||||
|
||||
@@ -83,7 +83,7 @@ TEST(SparseSetNoType, Pagination) {
|
||||
entt::sparse_set<std::uint64_t> set;
|
||||
constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(std::uint64_t);
|
||||
|
||||
ASSERT_EQ(set.extent(), 0u);
|
||||
ASSERT_EQ(set.extent(), 0);
|
||||
|
||||
set.construct(entt_per_page-1);
|
||||
|
||||
@@ -96,6 +96,23 @@ TEST(SparseSetNoType, Pagination) {
|
||||
ASSERT_TRUE(set.has(entt_per_page-1));
|
||||
ASSERT_TRUE(set.has(entt_per_page));
|
||||
ASSERT_FALSE(set.has(entt_per_page+1));
|
||||
|
||||
set.destroy(entt_per_page-1);
|
||||
|
||||
ASSERT_EQ(set.extent(), 2 * entt_per_page);
|
||||
ASSERT_FALSE(set.has(entt_per_page-1));
|
||||
ASSERT_TRUE(set.has(entt_per_page));
|
||||
|
||||
set.shrink_to_fit();
|
||||
set.destroy(entt_per_page);
|
||||
|
||||
ASSERT_EQ(set.extent(), 2 * entt_per_page);
|
||||
ASSERT_FALSE(set.has(entt_per_page-1));
|
||||
ASSERT_FALSE(set.has(entt_per_page));
|
||||
|
||||
set.shrink_to_fit();
|
||||
|
||||
ASSERT_EQ(set.extent(), 0);
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, BatchAdd) {
|
||||
@@ -419,33 +436,33 @@ TEST(SparseSetWithType, Functionalities) {
|
||||
ASSERT_EQ(std::as_const(set).begin(), std::as_const(set).end());
|
||||
ASSERT_EQ(set.begin(), set.end());
|
||||
ASSERT_FALSE(set.has(0));
|
||||
ASSERT_FALSE(set.has(42));
|
||||
ASSERT_FALSE(set.has(41));
|
||||
|
||||
set.construct(42, 3);
|
||||
set.construct(41, 3);
|
||||
|
||||
ASSERT_FALSE(set.empty());
|
||||
ASSERT_EQ(set.size(), 1u);
|
||||
ASSERT_NE(std::as_const(set).begin(), std::as_const(set).end());
|
||||
ASSERT_NE(set.begin(), set.end());
|
||||
ASSERT_FALSE(set.has(0));
|
||||
ASSERT_TRUE(set.has(42));
|
||||
ASSERT_EQ(set.get(42), 3);
|
||||
ASSERT_EQ(*set.try_get(42), 3);
|
||||
ASSERT_TRUE(set.has(41));
|
||||
ASSERT_EQ(set.get(41), 3);
|
||||
ASSERT_EQ(*set.try_get(41), 3);
|
||||
ASSERT_EQ(set.try_get(99), nullptr);
|
||||
|
||||
set.destroy(42);
|
||||
set.destroy(41);
|
||||
|
||||
ASSERT_TRUE(set.empty());
|
||||
ASSERT_EQ(set.size(), 0u);
|
||||
ASSERT_EQ(std::as_const(set).begin(), std::as_const(set).end());
|
||||
ASSERT_EQ(set.begin(), set.end());
|
||||
ASSERT_FALSE(set.has(0));
|
||||
ASSERT_FALSE(set.has(42));
|
||||
ASSERT_FALSE(set.has(41));
|
||||
|
||||
set.construct(42, 12);
|
||||
set.construct(41, 12);
|
||||
|
||||
ASSERT_EQ(set.get(42), 12);
|
||||
ASSERT_EQ(*set.try_get(42), 12);
|
||||
ASSERT_EQ(set.get(41), 12);
|
||||
ASSERT_EQ(*set.try_get(41), 12);
|
||||
ASSERT_EQ(set.try_get(99), nullptr);
|
||||
|
||||
set.reset();
|
||||
@@ -455,7 +472,13 @@ TEST(SparseSetWithType, Functionalities) {
|
||||
ASSERT_EQ(std::as_const(set).begin(), std::as_const(set).end());
|
||||
ASSERT_EQ(set.begin(), set.end());
|
||||
ASSERT_FALSE(set.has(0));
|
||||
ASSERT_FALSE(set.has(42));
|
||||
ASSERT_FALSE(set.has(41));
|
||||
|
||||
ASSERT_EQ(set.capacity(), 42);
|
||||
|
||||
set.shrink_to_fit();
|
||||
|
||||
ASSERT_EQ(set.capacity(), 0);
|
||||
|
||||
(void)entt::sparse_set<std::uint64_t, int>{std::move(set)};
|
||||
entt::sparse_set<std::uint64_t, int> other;
|
||||
|
||||
Reference in New Issue
Block a user