Support creating new ecs entities while iterating over them
This commit is contained in:
parent
d7babe6a0f
commit
3701df15f4
3 changed files with 93 additions and 1 deletions
|
|
@ -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_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue