ECS library wip: rewrite tables using explicit columns
This commit is contained in:
parent
1d20bd5a17
commit
59c803d31c
10 changed files with 383 additions and 296 deletions
|
|
@ -66,7 +66,7 @@ namespace psemek::ecs::detail
|
|||
std::size_t row_count;
|
||||
entity_handle const * entity_handles_pointer;
|
||||
// (+1) to prevent zero-sized array
|
||||
table::component_pointer pointers[sizeof...(Components) + 1];
|
||||
std::uint8_t * pointers[sizeof...(Components) + 1];
|
||||
|
||||
static_apply_helper(entity_container & parent, util::span<entity_handle const> entity_handles)
|
||||
: parent(parent)
|
||||
|
|
@ -83,7 +83,7 @@ namespace psemek::ecs::detail
|
|||
template <typename Function, std::size_t ... I>
|
||||
void apply_impl(Function && function, std::index_sequence<I...>)
|
||||
{
|
||||
invoke(function, parent, *entity_handles_pointer, *reinterpret_cast<Components *>(pointers[I].data) ...);
|
||||
invoke(function, parent, *entity_handles_pointer, *reinterpret_cast<Components *>(pointers[I]) ...);
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
|
|
@ -95,7 +95,7 @@ namespace psemek::ecs::detail
|
|||
template <typename Function, std::size_t ... I>
|
||||
void batch_apply_impl(Function && function, std::index_sequence<I...>)
|
||||
{
|
||||
batch_invoke(function, parent, row_count, entity_handles_pointer, reinterpret_cast<Components *>(pointers[I].data) ...);
|
||||
batch_invoke(function, parent, row_count, entity_handles_pointer, reinterpret_cast<Components *>(pointers[I]) ...);
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
|
|
@ -108,7 +108,7 @@ namespace psemek::ecs::detail
|
|||
++entity_handles_pointer;
|
||||
|
||||
std::size_t i = 0;
|
||||
((pointers[i++].data += stride<Components>()), ...);
|
||||
((pointers[i++] += stride<Components>()), ...);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
219
libs/ecs/include/psemek/ecs/detail/column.hpp
Normal file
219
libs/ecs/include/psemek/ecs/detail/column.hpp
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/detail/stride.hpp>
|
||||
#include <psemek/util/uuid.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
struct column
|
||||
{
|
||||
column(util::uuid const & uuid)
|
||||
: uuid_(uuid)
|
||||
{}
|
||||
|
||||
std::uint8_t * data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
util::uuid const & uuid() const
|
||||
{
|
||||
return uuid_;
|
||||
}
|
||||
|
||||
virtual void push_row() = 0;
|
||||
virtual void emplace_rows(std::uint8_t * data, std::size_t count) = 0;
|
||||
virtual void swap_rows(std::size_t row1, std::size_t row2) = 0;
|
||||
virtual void pop_row() = 0;
|
||||
virtual void clear() = 0;
|
||||
|
||||
virtual std::unique_ptr<column> clone() const = 0;
|
||||
|
||||
virtual ~column() = default;
|
||||
|
||||
protected:
|
||||
std::uint8_t * data_ = nullptr;
|
||||
util::uuid uuid_;
|
||||
};
|
||||
|
||||
template <typename Component, bool Empty = detail::is_empty_v<Component>>
|
||||
struct column_impl
|
||||
: column
|
||||
{
|
||||
column_impl();
|
||||
|
||||
void push_row() override;
|
||||
void emplace_rows(std::uint8_t * data, std::size_t count) override;
|
||||
void swap_rows(std::size_t row1, std::size_t row2) override;
|
||||
void pop_row() override;
|
||||
void clear() override;
|
||||
|
||||
std::unique_ptr<column> clone() const override;
|
||||
|
||||
~column_impl() override;
|
||||
|
||||
private:
|
||||
std::size_t capacity_ = 0;
|
||||
std::size_t row_count_ = 0;
|
||||
|
||||
void allocate(std::size_t min_capacity);
|
||||
};
|
||||
|
||||
template <typename Component>
|
||||
struct column_impl<Component, true>
|
||||
: column
|
||||
{
|
||||
column_impl();
|
||||
|
||||
void push_row() override;
|
||||
void emplace_rows(std::uint8_t * data, std::size_t count) override;
|
||||
void swap_rows(std::size_t row1, std::size_t row2) override;
|
||||
void pop_row() override;
|
||||
void clear() override;
|
||||
|
||||
std::unique_ptr<column> clone() const override;
|
||||
|
||||
~column_impl() override;
|
||||
};
|
||||
|
||||
template <typename Component, bool Empty>
|
||||
column_impl<Component, Empty>::column_impl()
|
||||
: column(Component::uuid())
|
||||
{}
|
||||
|
||||
template <typename Component, bool Empty>
|
||||
void column_impl<Component, Empty>::push_row()
|
||||
{
|
||||
allocate(row_count_ + 1);
|
||||
|
||||
new (reinterpret_cast<Component *>(data_) + row_count_) Component{};
|
||||
++row_count_;
|
||||
}
|
||||
|
||||
template <typename Component, bool Empty>
|
||||
void column_impl<Component, Empty>::emplace_rows(std::uint8_t * data, std::size_t count)
|
||||
{
|
||||
allocate(row_count_ + count);
|
||||
|
||||
auto src = reinterpret_cast<Component *>(data);
|
||||
auto dst = reinterpret_cast<Component *>(data_);
|
||||
auto src_end = src + count;
|
||||
while (src != src_end)
|
||||
{
|
||||
new (dst) Component{std::move(*src)};
|
||||
++src;
|
||||
++dst;
|
||||
}
|
||||
|
||||
row_count_ += count;
|
||||
}
|
||||
|
||||
template <typename Component, bool Empty>
|
||||
void column_impl<Component, Empty>::swap_rows(std::size_t row1, std::size_t row2)
|
||||
{
|
||||
auto data = reinterpret_cast<Component *>(data_);
|
||||
std::swap(data[row1], data[row2]);
|
||||
}
|
||||
|
||||
template <typename Component, bool Empty>
|
||||
void column_impl<Component, Empty>::pop_row()
|
||||
{
|
||||
(reinterpret_cast<Component *>(data_) + row_count_ - 1)->~Component();
|
||||
--row_count_;
|
||||
}
|
||||
|
||||
template <typename Component, bool Empty>
|
||||
void column_impl<Component, Empty>::clear()
|
||||
{
|
||||
auto data = reinterpret_cast<Component *>(data_);
|
||||
for (auto p = data; p < data + row_count_; ++p)
|
||||
p->~Component();
|
||||
row_count_ = 0;
|
||||
}
|
||||
|
||||
template <typename Component, bool Empty>
|
||||
std::unique_ptr<column> column_impl<Component, Empty>::clone() const
|
||||
{
|
||||
return std::make_unique<column_impl<Component, Empty>>();
|
||||
}
|
||||
|
||||
template <typename Component, bool Empty>
|
||||
column_impl<Component, Empty>::~column_impl()
|
||||
{
|
||||
clear();
|
||||
delete [] data_;
|
||||
data_ = nullptr;
|
||||
row_count_ = 0;
|
||||
}
|
||||
|
||||
template <typename Component, bool Empty>
|
||||
void column_impl<Component, Empty>::allocate(std::size_t min_capacity)
|
||||
{
|
||||
if (capacity_ >= min_capacity)
|
||||
return;
|
||||
|
||||
std::size_t new_capacity = std::max<std::size_t>(64, capacity_);
|
||||
while (new_capacity < min_capacity)
|
||||
new_capacity *= 2;
|
||||
|
||||
auto new_data = new (std::align_val_t(alignof(Component))) std::uint8_t[new_capacity * sizeof(Component)];
|
||||
|
||||
auto old_begin = reinterpret_cast<Component *>(data_);
|
||||
auto old_end = old_begin + row_count_;
|
||||
auto new_begin = reinterpret_cast<Component *>(new_data);
|
||||
for (; old_begin != old_end; ++old_begin, ++new_begin)
|
||||
{
|
||||
new (new_begin) Component{std::move(*old_begin)};
|
||||
old_begin->~Component();
|
||||
}
|
||||
|
||||
delete [] data_;
|
||||
data_ = new_data;
|
||||
capacity_ = new_capacity;
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
column_impl<Component, true>::column_impl()
|
||||
: column(Component::uuid())
|
||||
{
|
||||
data_ = reinterpret_cast<std::uint8_t *>(new Component[1]);
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
void column_impl<Component, true>::push_row()
|
||||
{}
|
||||
|
||||
template <typename Component>
|
||||
void column_impl<Component, true>::emplace_rows(std::uint8_t *, std::size_t)
|
||||
{}
|
||||
|
||||
template <typename Component>
|
||||
void column_impl<Component, true>::swap_rows(std::size_t, std::size_t)
|
||||
{}
|
||||
|
||||
template <typename Component>
|
||||
void column_impl<Component, true>::pop_row()
|
||||
{}
|
||||
|
||||
template <typename Component>
|
||||
void column_impl<Component, true>::clear()
|
||||
{}
|
||||
|
||||
template <typename Component>
|
||||
std::unique_ptr<column> column_impl<Component, true>::clone() const
|
||||
{
|
||||
return std::make_unique<column_impl<Component, true>>();
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
column_impl<Component, true>::~column_impl()
|
||||
{
|
||||
delete [] reinterpret_cast<Component *>(data_);
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,18 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/uuid.hpp>
|
||||
#include <psemek/util/span.hpp>
|
||||
#include <psemek/util/hash.hpp>
|
||||
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
inline std::size_t component_hash(util::span<util::uuid const> const & uuids)
|
||||
struct component_hasher
|
||||
{
|
||||
std::size_t result = 0xcd5694d2b3f3443eull;
|
||||
for (auto const & uuid : uuids)
|
||||
|
||||
void operator()(util::uuid const & uuid)
|
||||
{
|
||||
result ^= std::hash<util::uuid>{}(uuid);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename UUIDs>
|
||||
std::size_t component_hash(UUIDs const & uuids)
|
||||
{
|
||||
component_hasher hasher;
|
||||
for (auto const & uuid : uuids)
|
||||
hasher(uuid);
|
||||
return hasher.result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,17 +8,18 @@ namespace psemek::ecs::detail
|
|||
{
|
||||
|
||||
struct table;
|
||||
struct column;
|
||||
|
||||
struct query_cache_entry
|
||||
{
|
||||
struct table * table = nullptr;
|
||||
std::vector<std::size_t> column_ids;
|
||||
std::vector<column *> columns;
|
||||
};
|
||||
|
||||
struct query_cache
|
||||
{
|
||||
std::vector<util::uuid> component_uuids;
|
||||
std::vector<query_cache_entry> tables;
|
||||
std::vector<query_cache_entry> entries;
|
||||
|
||||
void add(table * table);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/entity_handle.hpp>
|
||||
#include <psemek/ecs/detail/stride.hpp>
|
||||
#include <psemek/ecs/detail/component_hash.hpp>
|
||||
#include <psemek/ecs/detail/column.hpp>
|
||||
#include <psemek/util/uuid.hpp>
|
||||
#include <psemek/util/span.hpp>
|
||||
#include <psemek/util/hash_table.hpp>
|
||||
|
|
@ -19,308 +19,62 @@ namespace psemek::ecs::detail
|
|||
|
||||
struct table
|
||||
{
|
||||
struct component_pointer
|
||||
{
|
||||
std::uint8_t * data = nullptr;
|
||||
};
|
||||
|
||||
struct iteration_data
|
||||
{
|
||||
std::size_t current_row = 0;
|
||||
};
|
||||
table(std::vector<std::unique_ptr<detail::column>> columns);
|
||||
|
||||
std::size_t hash() const
|
||||
{
|
||||
return hash_;
|
||||
}
|
||||
|
||||
std::optional<std::size_t> component_column(util::uuid const & uuid) const
|
||||
{
|
||||
if (auto it = component_uuid_to_column_.find(uuid); it != component_uuid_to_column_.end())
|
||||
return it->second;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
util::span<component_pointer const> component_pointers() const
|
||||
{
|
||||
return component_pointers_;
|
||||
}
|
||||
detail::column * column(util::uuid const & uuid) const;
|
||||
|
||||
util::span<entity_handle const> entity_handles() const
|
||||
{
|
||||
return entity_handles_;
|
||||
}
|
||||
|
||||
std::size_t component_count() const
|
||||
std::size_t column_count() const
|
||||
{
|
||||
return component_pointers_.size();
|
||||
return component_uuid_to_column_.size();
|
||||
}
|
||||
|
||||
std::size_t row_count() const
|
||||
{
|
||||
return row_count_;
|
||||
return entity_handles_.size();
|
||||
}
|
||||
|
||||
virtual std::size_t push_row(entity_handle handles) = 0;
|
||||
virtual void swap_rows(std::size_t row1, std::size_t row2) = 0;
|
||||
virtual void pop_row() = 0;
|
||||
std::size_t push_row(entity_handle handle);
|
||||
void swap_rows(std::size_t row1, std::size_t row2);
|
||||
void pop_row();
|
||||
void clear();
|
||||
|
||||
virtual void clear() = 0;
|
||||
struct iteration_data
|
||||
{
|
||||
std::size_t current_row = 0;
|
||||
};
|
||||
|
||||
std::optional<iteration_data> & get_iteration_data()
|
||||
{
|
||||
return iteration_data_;
|
||||
}
|
||||
|
||||
void push_remove(std::uint32_t row)
|
||||
{
|
||||
remove_queue_.push_back(row);
|
||||
}
|
||||
void push_remove(std::uint32_t row);
|
||||
std::vector<std::uint32_t> grab_remove_queue();
|
||||
|
||||
std::vector<std::uint32_t> grab_remove_queue()
|
||||
{
|
||||
return std::move(remove_queue_);
|
||||
}
|
||||
std::unique_ptr<table> clone() const;
|
||||
|
||||
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;
|
||||
table * get_delayed_table();
|
||||
util::span<entity_handle const> flush_delayed();
|
||||
|
||||
protected:
|
||||
std::size_t hash_;
|
||||
std::vector<util::uuid> component_uuids_;
|
||||
util::hash_map<util::uuid, std::size_t> component_uuid_to_column_;
|
||||
std::vector<component_pointer> component_pointers_;
|
||||
std::size_t row_count_ = 0;
|
||||
util::hash_map<util::uuid, std::unique_ptr<detail::column>> component_uuid_to_column_;
|
||||
|
||||
std::vector<entity_handle> entity_handles_;
|
||||
|
||||
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>
|
||||
struct table_impl
|
||||
: table
|
||||
{
|
||||
table_impl(util::span<util::uuid const> component_uuids);
|
||||
|
||||
std::size_t push_row(entity_handle handle) override;
|
||||
void swap_rows(std::size_t row1, std::size_t row2) override;
|
||||
void pop_row() override;
|
||||
|
||||
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>
|
||||
table_impl<Components...>::table_impl(util::span<util::uuid const> component_uuids)
|
||||
{
|
||||
assert(sizeof...(Components) == component_uuids.size());
|
||||
hash_ = component_hash(component_uuids);
|
||||
component_uuids_.assign(component_uuids.begin(), component_uuids.end());
|
||||
component_pointers_.resize(sizeof...(Components));
|
||||
for (std::size_t i = 0; i < component_uuids.size(); ++i)
|
||||
component_uuid_to_column_.insert({component_uuids[i], i});
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
std::size_t table_impl<Components...>::push_row(entity_handle handle)
|
||||
{
|
||||
if (row_count_ == capacity_)
|
||||
reallocate();
|
||||
|
||||
[[maybe_unused]] auto push_row_impl = [&]<typename Component>(std::uint8_t * data)
|
||||
{
|
||||
if constexpr (!detail::is_empty_v<Component>)
|
||||
{
|
||||
new (reinterpret_cast<Component *>(data) + row_count_) Component{};
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(push_row_impl.template operator()<Components>(component_pointers_[i++].data), ...);
|
||||
++row_count_;
|
||||
|
||||
entity_handles_.push_back(handle);
|
||||
|
||||
return row_count_ - 1;
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void table_impl<Components...>::swap_rows(std::size_t row1, std::size_t row2)
|
||||
{
|
||||
[[maybe_unused]] auto swap_rows_impl = [&]<typename Component>(std::uint8_t * data)
|
||||
{
|
||||
if constexpr (!detail::is_empty_v<Component>)
|
||||
{
|
||||
auto cdata = reinterpret_cast<Component *>(data);
|
||||
std::iter_swap(cdata + row1, cdata + row2);
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(swap_rows_impl.template operator()<Components>(component_pointers_[i++].data), ...);
|
||||
|
||||
std::swap(entity_handles_[row1], entity_handles_[row2]);
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void table_impl<Components...>::pop_row()
|
||||
{
|
||||
--row_count_;
|
||||
[[maybe_unused]] auto pop_row_impl = [&]<typename Component>(std::uint8_t * data)
|
||||
{
|
||||
if constexpr (!detail::is_empty_v<Component>)
|
||||
{
|
||||
(reinterpret_cast<Component *>(data) + row_count_)->~Component();
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(pop_row_impl.template operator()<Components>(component_pointers_[i++].data), ...);
|
||||
entity_handles_.pop_back();
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void table_impl<Components...>::clear()
|
||||
{
|
||||
[[maybe_unused]] auto clear_column_impl = [&]<typename Component>(std::uint8_t * data)
|
||||
{
|
||||
auto ptr = reinterpret_cast<Component *>(data);
|
||||
if constexpr (!detail::is_empty_v<Component>)
|
||||
{
|
||||
for (std::size_t i = 0; i < row_count_; ++i)
|
||||
ptr[i].~Component();
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(clear_column_impl.template operator()<Components>(component_pointers_[i++].data), ...);
|
||||
|
||||
row_count_ = 0;
|
||||
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()
|
||||
{
|
||||
clear();
|
||||
|
||||
[[maybe_unused]] auto delete_column_impl = [&]<typename Component>(std::uint8_t * & data)
|
||||
{
|
||||
if constexpr (detail::is_empty_v<Component>)
|
||||
{
|
||||
reinterpret_cast<Component *>(data)->~Component();
|
||||
}
|
||||
delete [] data;
|
||||
data = nullptr;
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(delete_column_impl.template operator()<Components>(component_pointers_[i++].data), ...);
|
||||
|
||||
capacity_ = 0;
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void table_impl<Components...>::reallocate()
|
||||
{
|
||||
std::size_t const new_capacity = capacity_ == 0 ? 64 : capacity_ * 2;
|
||||
|
||||
[[maybe_unused]] auto reallocate_impl = [&]<typename Component>(std::uint8_t * & data)
|
||||
{
|
||||
if constexpr (detail::is_empty_v<Component>)
|
||||
{
|
||||
if (!data)
|
||||
data = reinterpret_cast<std::uint8_t *>(new Component[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto new_data = new (std::align_val_t(alignof(Component))) std::uint8_t[new_capacity * sizeof(Component)];
|
||||
|
||||
auto old_begin = reinterpret_cast<Component *>(data);
|
||||
auto old_end = old_begin + row_count_;
|
||||
auto new_begin = reinterpret_cast<Component *>(new_data);
|
||||
for (; old_begin != old_end; ++old_begin, ++new_begin)
|
||||
{
|
||||
new (new_begin) Component{std::move(*old_begin)};
|
||||
old_begin->~Component();
|
||||
}
|
||||
|
||||
delete [] data;
|
||||
data = new_data;
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(reallocate_impl.template operator()<Components>(component_pointers_[i++].data), ...);
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ namespace psemek::ecs::detail
|
|||
{
|
||||
bool operator()(util::span<util::uuid const> const & uuids, std::unique_ptr<table> const & table) const
|
||||
{
|
||||
if (uuids.size() != table->component_count())
|
||||
if (uuids.size() != table->column_count())
|
||||
return false;
|
||||
for (auto const & uuid : uuids)
|
||||
if (!table->component_column(uuid))
|
||||
if (!table->column(uuid))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -60,7 +60,10 @@ namespace psemek::ecs::detail
|
|||
if (it != tables_.end())
|
||||
return {it->get(), false};
|
||||
|
||||
auto table = std::make_unique<table_impl<Components...>>(component_uuids);
|
||||
std::vector<std::unique_ptr<column>> columns;
|
||||
(columns.push_back(std::make_unique<column_impl<Components>>()), ...);
|
||||
|
||||
auto table = std::make_unique<detail::table>(std::move(columns));
|
||||
auto result = table.get();
|
||||
tables_.insert(std::move(table));
|
||||
return {result, true};
|
||||
|
|
@ -73,7 +76,7 @@ namespace psemek::ecs::detail
|
|||
{
|
||||
bool good = true;
|
||||
for (auto const & uuid : component_uuids)
|
||||
if (!table->component_column(uuid))
|
||||
if (!table->column(uuid))
|
||||
{
|
||||
good = false;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -192,6 +192,8 @@ namespace psemek::ecs
|
|||
detail::table_container table_container_;
|
||||
detail::query_cache_container query_cache_container_;
|
||||
|
||||
std::vector<util::uuid> uuid_helper_;
|
||||
|
||||
void remove_row(detail::table & table, std::uint32_t row, util::span<detail::entity_data> entities);
|
||||
};
|
||||
|
||||
|
|
@ -210,7 +212,7 @@ namespace psemek::ecs
|
|||
{
|
||||
query_cache_container_.apply([table](detail::query_cache & cache){
|
||||
cache.add(table);
|
||||
}, [table](util::uuid const & uuid){ return table->component_column(uuid) != std::nullopt; });
|
||||
}, [table](util::uuid const & uuid){ return table->column(uuid) != nullptr; });
|
||||
}
|
||||
|
||||
if (table->get_iteration_data())
|
||||
|
|
@ -246,14 +248,14 @@ namespace psemek::ecs
|
|||
if (!cache)
|
||||
cache = this->cache<Components...>();
|
||||
|
||||
for (auto const & entry : cache->tables)
|
||||
for (auto const & entry : cache->entries)
|
||||
{
|
||||
auto & iteration_data = entry.table->get_iteration_data();
|
||||
iteration_data.emplace();
|
||||
detail::static_apply_helper<Components...> apply_helper(*this, entry.table->entity_handles());
|
||||
|
||||
for (std::size_t i = 0; i < sizeof...(Components); ++i)
|
||||
apply_helper.pointers[i] = entry.table->component_pointers()[entry.column_ids[i]];
|
||||
apply_helper.pointers[i] = entry.table->column(cache->component_uuids[i])->data();
|
||||
|
||||
for (std::size_t i = 0; i < entry.table->row_count(); ++i)
|
||||
{
|
||||
|
|
@ -286,12 +288,12 @@ namespace psemek::ecs
|
|||
if (!cache)
|
||||
cache = this->cache<Components...>();
|
||||
|
||||
for (auto const & entry : cache->tables)
|
||||
for (auto const & entry : cache->entries)
|
||||
{
|
||||
detail::static_apply_helper<Components...> apply_helper(*this, entry.table->entity_handles());
|
||||
|
||||
for (std::size_t i = 0; i < sizeof...(Components); ++i)
|
||||
apply_helper.pointers[i] = entry.table->component_pointers()[entry.column_ids[i]];
|
||||
apply_helper.pointers[i] = entry.table->column(cache->component_uuids[i])->data();
|
||||
|
||||
apply_helper.batch_apply(function);
|
||||
}
|
||||
|
|
@ -307,11 +309,11 @@ namespace psemek::ecs
|
|||
{
|
||||
util::uuid const uuid = Component::uuid();
|
||||
|
||||
auto column = table_->component_column(uuid);
|
||||
auto column = table_->column(uuid);
|
||||
if (!column)
|
||||
return nullptr;
|
||||
|
||||
return reinterpret_cast<Component *>(table_->component_pointers()[*column].data + detail::stride<Component>() * row_);
|
||||
return reinterpret_cast<Component *>(column->data() + detail::stride<Component>() * row_);
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ namespace psemek::ecs::detail
|
|||
|
||||
void query_cache::add(table * table)
|
||||
{
|
||||
auto & entry = tables.emplace_back();
|
||||
auto & entry = entries.emplace_back();
|
||||
entry.table = table;
|
||||
|
||||
for (auto const & uuid : component_uuids)
|
||||
entry.column_ids.push_back(*(table->component_column(uuid)));
|
||||
entry.columns.push_back(table->column(uuid));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
98
libs/ecs/source/detail/table.cpp
Normal file
98
libs/ecs/source/detail/table.cpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#include <psemek/ecs/detail/table.hpp>
|
||||
#include <psemek/ecs/detail/component_hash.hpp>
|
||||
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
table::table(std::vector<std::unique_ptr<detail::column>> columns)
|
||||
{
|
||||
component_hasher hasher;
|
||||
for (auto & column : columns)
|
||||
{
|
||||
auto uuid = column->uuid();
|
||||
hasher(uuid);
|
||||
component_uuid_to_column_.insert({uuid, std::move(column)});
|
||||
}
|
||||
hash_ = hasher.result;
|
||||
}
|
||||
|
||||
detail::column * table::column(util::uuid const & uuid) const
|
||||
{
|
||||
if (auto it = component_uuid_to_column_.find(uuid); it != component_uuid_to_column_.end())
|
||||
return it->second.get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::size_t table::push_row(entity_handle handle)
|
||||
{
|
||||
for (auto & pair : component_uuid_to_column_)
|
||||
pair.second->push_row();
|
||||
entity_handles_.push_back(handle);
|
||||
return entity_handles_.size() - 1;
|
||||
}
|
||||
|
||||
void table::swap_rows(std::size_t row1, std::size_t row2)
|
||||
{
|
||||
for (auto & pair : component_uuid_to_column_)
|
||||
pair.second->swap_rows(row1, row2);
|
||||
std::swap(entity_handles_[row1], entity_handles_[row2]);
|
||||
}
|
||||
|
||||
void table::pop_row()
|
||||
{
|
||||
for (auto & pair : component_uuid_to_column_)
|
||||
pair.second->pop_row();
|
||||
entity_handles_.pop_back();
|
||||
}
|
||||
|
||||
void table::clear()
|
||||
{
|
||||
for (auto & pair : component_uuid_to_column_)
|
||||
pair.second->clear();
|
||||
entity_handles_.clear();
|
||||
}
|
||||
|
||||
void table::push_remove(std::uint32_t row)
|
||||
{
|
||||
remove_queue_.push_back(row);
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> table::grab_remove_queue()
|
||||
{
|
||||
return std::move(remove_queue_);
|
||||
}
|
||||
|
||||
std::unique_ptr<table> table::clone() const
|
||||
{
|
||||
std::vector<std::unique_ptr<detail::column>> columns;
|
||||
for (auto const & pair : component_uuid_to_column_)
|
||||
columns.push_back(pair.second->clone());
|
||||
return std::make_unique<table>(std::move(columns));
|
||||
}
|
||||
|
||||
table * table::get_delayed_table()
|
||||
{
|
||||
if (!delayed_table_)
|
||||
delayed_table_ = clone();
|
||||
return delayed_table_.get();
|
||||
}
|
||||
|
||||
util::span<entity_handle const> table::flush_delayed()
|
||||
{
|
||||
if (!delayed_table_)
|
||||
return {};
|
||||
|
||||
std::size_t count = delayed_table_->row_count();
|
||||
for (auto & pair : component_uuid_to_column_)
|
||||
{
|
||||
auto * src_column = delayed_table_->column(pair.second->uuid());
|
||||
pair.second->emplace_rows(src_column->data(), count);
|
||||
}
|
||||
entity_handles_.insert(entity_handles_.end(), delayed_table_->entity_handles_.begin(), delayed_table_->entity_handles_.end());
|
||||
|
||||
delayed_table_->clear();
|
||||
|
||||
return {entity_handles_.data() + entity_handles_.size() - count, count};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ test_case(ecs_cache_empty)
|
|||
|
||||
expect_different_ptr(cache.get(), nullptr);
|
||||
expect(cache->component_uuids.empty());
|
||||
expect_equal(cache->tables.size(), 4);
|
||||
expect_equal(cache->entries.size(), 4);
|
||||
}
|
||||
|
||||
test_case(ecs_cache_components)
|
||||
|
|
@ -61,7 +61,7 @@ test_case(ecs_cache_components)
|
|||
|
||||
expect_different_ptr(cache.get(), nullptr);
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->tables.size(), 2);
|
||||
expect_equal(cache->entries.size(), 2);
|
||||
}
|
||||
|
||||
test_case(ecs_cache_update)
|
||||
|
|
@ -72,23 +72,23 @@ test_case(ecs_cache_update)
|
|||
|
||||
expect_different_ptr(cache.get(), nullptr);
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->tables.size(), 0);
|
||||
expect_equal(cache->entries.size(), 0);
|
||||
|
||||
container.create();
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->tables.size(), 0);
|
||||
expect_equal(cache->entries.size(), 0);
|
||||
|
||||
container.create(component_1{10});
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->tables.size(), 1);
|
||||
expect_equal(cache->entries.size(), 1);
|
||||
|
||||
container.create(component_2{20});
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->tables.size(), 1);
|
||||
expect_equal(cache->entries.size(), 1);
|
||||
|
||||
container.create(component_1{100}, component_2{200});
|
||||
expect_equal(cache->component_uuids.size(), 1);
|
||||
expect_equal(cache->tables.size(), 2);
|
||||
expect_equal(cache->entries.size(), 2);
|
||||
}
|
||||
|
||||
test_case(ecs_cache_apply)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue