diff --git a/libs/ecs/include/psemek/ecs/detail/table.hpp b/libs/ecs/include/psemek/ecs/detail/table.hpp index 5465adfa..84687baf 100644 --- a/libs/ecs/include/psemek/ecs/detail/table.hpp +++ b/libs/ecs/include/psemek/ecs/detail/table.hpp @@ -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 flush_delayed() = 0; + virtual ~table() = default; protected: @@ -85,6 +94,10 @@ namespace psemek::ecs::detail std::optional iteration_data_; std::vector remove_queue_; + + std::unique_ptr delayed_table_; + + virtual std::unique_ptr
create_delayed_table() = 0; }; template @@ -99,12 +112,16 @@ namespace psemek::ecs::detail void clear() override; + util::span flush_delayed() override; + ~table_impl() override; private: std::size_t capacity_ = 0; void reallocate(); + + std::unique_ptr
create_delayed_table() override; }; template @@ -182,7 +199,7 @@ namespace psemek::ecs::detail if constexpr (!detail::is_empty_v) { 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 + util::span table_impl::flush_delayed() + { + if (!delayed_table_) + return {}; + + auto delayed = static_cast *>(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 = [&](std::uint8_t * data_tgt, std::uint8_t * data_src) + { + if constexpr (!detail::is_empty_v) + { + new (reinterpret_cast(data_tgt) + row_count_) Component{std::move(*(reinterpret_cast(data_src) + delayed_row))}; + } + }; + + for (; delayed_row < delayed->row_count_; ++delayed_row) + { + std::size_t i1 = 0, i2 = 0; + (move_row_impl.template operator()(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 table_impl::~table_impl() { @@ -250,4 +303,10 @@ namespace psemek::ecs::detail capacity_ = new_capacity; } + template + std::unique_ptr
table_impl::create_delayed_table() + { + return std::make_unique>(component_uuids_); + } + } diff --git a/libs/ecs/include/psemek/ecs/entity_container.hpp b/libs/ecs/include/psemek/ecs/entity_container.hpp index 83ad9add..c3ddf5fe 100644 --- a/libs/ecs/include/psemek/ecs/entity_container.hpp +++ b/libs/ecs/include/psemek/ecs/entity_container.hpp @@ -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++; + } } } diff --git a/libs/ecs/tests/apply.cpp b/libs/ecs/tests/apply.cpp index 547bae04..da14a8f2 100644 --- a/libs/ecs/tests/apply.cpp +++ b/libs/ecs/tests/apply.cpp @@ -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); +}