Support creating new ecs entities while iterating over them

This commit is contained in:
Nikita Lisitsa 2023-08-23 17:46:50 +03:00
parent d7babe6a0f
commit 3701df15f4
3 changed files with 93 additions and 1 deletions

View file

@ -73,6 +73,15 @@ namespace psemek::ecs::detail
return std::move(remove_queue_);
}
table * get_delayed_table()
{
if (!delayed_table_)
delayed_table_ = create_delayed_table();
return delayed_table_.get();
}
virtual util::span<entity_handle const> flush_delayed() = 0;
virtual ~table() = default;
protected:
@ -85,6 +94,10 @@ namespace psemek::ecs::detail
std::optional<iteration_data> iteration_data_;
std::vector<std::uint32_t> remove_queue_;
std::unique_ptr<table> delayed_table_;
virtual std::unique_ptr<table> create_delayed_table() = 0;
};
template <typename ... Components>
@ -99,12 +112,16 @@ namespace psemek::ecs::detail
void clear() override;
util::span<entity_handle const> flush_delayed() override;
~table_impl() override;
private:
std::size_t capacity_ = 0;
void reallocate();
std::unique_ptr<table> create_delayed_table() override;
};
template <typename ... Components>
@ -182,7 +199,7 @@ namespace psemek::ecs::detail
if constexpr (!detail::is_empty_v<Component>)
{
for (std::size_t i = 0; i < row_count_; ++i)
ptr[i]->~Component();
ptr[i].~Component();
}
};
@ -193,6 +210,42 @@ namespace psemek::ecs::detail
entity_handles_.clear();
}
template <typename ... Components>
util::span<entity_handle const> table_impl<Components...>::flush_delayed()
{
if (!delayed_table_)
return {};
auto delayed = static_cast<table_impl<Components...> *>(delayed_table_.get());
auto old_row_count = row_count_;
while (capacity_ < row_count_ + delayed->row_count_)
reallocate();
std::size_t delayed_row = 0;
[[maybe_unused]] auto move_row_impl = [&]<typename Component>(std::uint8_t * data_tgt, std::uint8_t * data_src)
{
if constexpr (!detail::is_empty_v<Component>)
{
new (reinterpret_cast<Component *>(data_tgt) + row_count_) Component{std::move(*(reinterpret_cast<Component *>(data_src) + delayed_row))};
}
};
for (; delayed_row < delayed->row_count_; ++delayed_row)
{
std::size_t i1 = 0, i2 = 0;
(move_row_impl.template operator()<Components>(component_pointers_[i1++].data, delayed->component_pointers_[i2++].data), ...);
++row_count_;
}
entity_handles_.insert(entity_handles_.end(), delayed->entity_handles_.begin(), delayed->entity_handles_.end());
delayed->clear();
return {entity_handles_.data() + old_row_count, entity_handles_.data() + row_count_};
}
template <typename ... Components>
table_impl<Components...>::~table_impl()
{
@ -250,4 +303,10 @@ namespace psemek::ecs::detail
capacity_ = new_capacity;
}
template <typename ... Components>
std::unique_ptr<table> table_impl<Components...>::create_delayed_table()
{
return std::make_unique<table_impl<Components...>>(component_uuids_);
}
}

View file

@ -110,6 +110,9 @@ namespace psemek::ecs
}, mask);
}
if (table->get_iteration_data())
table = table->get_delayed_table();
auto id = entity_list_.create(table, table->row_count());
entity_handle handle{id, entity_list_.get_entities()[id].epoch};
[[maybe_unused]] entity_accessor accessor = get(handle);
@ -164,6 +167,14 @@ namespace psemek::ecs
auto entities = entity_list_.get_entities();
for (auto row : util::reversed(remove_queue))
remove_row(*entry.table, row, entities);
std::uint32_t row = entry.table->row_count();
for (auto const & handle : entry.table->flush_delayed())
{
auto & data = entities[handle.id];
data.table = entry.table;
data.row = row++;
}
}
}

View file

@ -239,3 +239,25 @@ test_case(ecs_apply_remove_random)
for (auto h : handles)
expect(!container.alive(h));
}
test_case(ecs_apply_create)
{
entity_container container;
int const count = 1024 * 1024;
for (int i = 0; i < count; ++i)
container.create();
int call_count = 0;
container.apply([&]{
++call_count;
container.create();
});
expect_equal(call_count, count);
call_count = 0;
container.apply([&]{
++call_count;
});
expect_equal(call_count, 2 * count);
}