@@ -3,6 +3,7 @@ | |||
#include <ecs/config.h> | |||
#include <ecs/core/utils/thread_pool.h> | |||
#include <ecs/core/utils/counter_blocker.h> | |||
#include <ecs/core/utils/ordered_vector.h> | |||
#include <ecs/core/component/manager.h> | |||
#include "./context.fwd.h" | |||
@@ -44,7 +45,7 @@ namespace context { | |||
using handle_type = entity_handle_type; | |||
private: | |||
using handle_vector_type = std::vector<handle_type>; | |||
using handle_set_type = core::utils::ordered_vector<handle_type>; | |||
protected: | |||
context_type& _context; //!< reference to its own child class | |||
@@ -56,8 +57,8 @@ namespace context { | |||
core::utils::thread_pool _thread_pool; //!< thread pool to execute tasks parallel | |||
handle_vector_type _to_match; //!< handles of entities that were created in the last tick | |||
handle_vector_type _to_kill; //!< handles of entities that were destroyed in the last tick | |||
handle_set_type _to_match; //!< handles of entities that were created in the last tick | |||
handle_set_type _to_kill; //!< handles of entities that were destroyed in the last tick | |||
public: | |||
@@ -111,6 +112,22 @@ namespace context { | |||
*/ | |||
inline bool _is_alive(const handle_type& handle) const; | |||
/** | |||
* destroy the passed entity | |||
* | |||
* @param handle handle of the entity to destroy | |||
*/ | |||
inline void _destroy_entity(const handle_type& handle); | |||
/** | |||
* get the meta data for the passed entity | |||
* | |||
* @param handle handle to get meta data for | |||
* | |||
* @return meta data of the passed entity | |||
*/ | |||
inline const auto& _entity_meta_data(const handle_type& handle); | |||
protected: /* component */ | |||
/** | |||
@@ -38,15 +38,16 @@ namespace detail { | |||
inline decltype(auto) base_t<T_settings> | |||
::_create_entity(T_args&&... args) | |||
{ | |||
_to_match.emplace_back(_entities.claim(std::forward<T_args>(args)...)); | |||
return _to_match.back(); | |||
auto handle = _entities.claim(std::forward<T_args>(args)...); | |||
_to_match.insert(handle); | |||
return handle; | |||
} | |||
template<typename T_settings> | |||
inline void base_t<T_settings> | |||
::_kill_entity(const handle_type& handle) | |||
{ | |||
_to_kill.emplace_back(handle); | |||
_to_kill.insert(handle); | |||
} | |||
template<typename T_settings> | |||
@@ -56,6 +57,20 @@ namespace detail { | |||
return _entities.is_valid(handle); | |||
} | |||
template<typename T_settings> | |||
inline void base_t<T_settings> | |||
::_destroy_entity(const handle_type& handle) | |||
{ | |||
_entities.reclaim(handle); | |||
} | |||
template<typename T_settings> | |||
inline const auto& base_t<T_settings> | |||
::_entity_meta_data(const handle_type& handle) | |||
{ | |||
return _entities.meta_data(handle); | |||
} | |||
/* components */ | |||
template<typename T_settings> | |||
@@ -63,7 +78,7 @@ namespace detail { | |||
inline decltype(auto) base_t<T_settings> | |||
::_add_component(const handle_type& handle, T_component_tag ct) | |||
{ | |||
_to_match.emplace_back(handle); | |||
_to_match.insert(handle); | |||
auto& meta = _entities.meta_data(handle); | |||
auto& cmd = meta.storage_meta_data(); | |||
auto& c = _components.add(ct, handle, cmd); | |||
@@ -18,16 +18,91 @@ namespace context { | |||
private: | |||
using settings_type = T_settings; | |||
using this_type = context_t<settings_type>; | |||
using base_type = step_proxy_t<settings_type>; | |||
using step_proxy = step_proxy_t<settings_type>; | |||
using defer_proxy = defer_proxy_t<settings_type>; | |||
using base_type = step_proxy; | |||
private: | |||
inline void refresh() | |||
{ | |||
refresh_execute_deferred(); | |||
refresh_kill_entities(); | |||
refresh_match_entities(); | |||
} | |||
inline void refresh_match_entities() | |||
{ | |||
/* match new/changed entities to system instances */ | |||
for_systems_dispatch([this](auto& instance){ | |||
auto& system_bitset = instance.bitset(); | |||
for (const auto& handle : this->_to_match) | |||
{ | |||
auto& entity_bitset = this->entity_meta_data(handle).bitset(); | |||
if (entity_bitset.contains(system_bitset)) | |||
{ | |||
if (instance.subscribe(handle)) | |||
{ | |||
// TODO send event | |||
} | |||
} | |||
else if (instance.unsubscribe(handle)) | |||
{ | |||
// TODO send event | |||
} | |||
} | |||
}); | |||
this->_to_match.clear(); | |||
} | |||
inline void refresh_kill_entities() | |||
{ | |||
/* copy entities to kill to the main vector */ | |||
for_systems_sequential([this](auto& instance){ | |||
instance.for_states([this](auto& state){ | |||
for (auto& handle : state.to_kill) | |||
{ | |||
this->_to_kill.insert(handle); | |||
} | |||
}); | |||
}); | |||
/* unsubscribe dead entities */ | |||
for_systems_dispatch([this](auto& instance){ | |||
for (const auto& handle : this->_to_kill) | |||
{ | |||
if (instance.unsubscribe(handle)) | |||
{ | |||
// TODO send event | |||
} | |||
} | |||
}); | |||
/* reclaim all killed entities */ | |||
for (const auto& handle : this->_to_kill) | |||
{ | |||
destroy_entity(handle); | |||
} | |||
this->_to_kill.clear(); | |||
} | |||
inline void refresh_execute_deferred() | |||
{ | |||
defer_proxy& proxy = *this; | |||
for_systems_sequential([&proxy](auto& instance){ | |||
instance.for_states([&proxy](auto& state){ | |||
state.deferred_functions.execute_all(proxy); | |||
}); | |||
}); | |||
} | |||
public: | |||
ecs_context_proxy_func(this_type, destroy_entity) | |||
ecs_context_proxy_func(this_type, post_in_thread_pool) | |||
ecs_context_proxy_func(this_type, for_systems_sequential) | |||
ecs_context_proxy_func(this_type, for_systems_parallel) | |||
ecs_context_proxy_func(this_type, for_systems_dispatch) | |||
public: | |||
inline context_t() | |||
@@ -33,6 +33,7 @@ namespace context { | |||
ecs_context_proxy_func(this_type, create_entity) | |||
ecs_context_proxy_func(this_type, kill_entity) | |||
ecs_context_proxy_func(this_type, is_alive) | |||
ecs_context_proxy_func(this_type, entity_meta_data) | |||
public: /* component */ | |||
ecs_context_proxy_func(this_type, add_component) | |||
@@ -105,6 +105,13 @@ namespace storage { | |||
*/ | |||
inline auto& bitset(); | |||
/** | |||
* get the bitset of components stored for this entity | |||
* | |||
* @return component bitset | |||
*/ | |||
inline const auto& bitset() const; | |||
/** | |||
* get the current reusage counter of the entity | |||
* | |||
@@ -58,6 +58,13 @@ namespace storage { | |||
return _bitset; | |||
} | |||
template <typename T_settings, typename T_storage_meta_data> | |||
inline const auto& entity_meta_data<T_settings, T_storage_meta_data> | |||
::bitset() const | |||
{ | |||
return _bitset; | |||
} | |||
template <typename T_settings, typename T_storage_meta_data> | |||
inline auto entity_meta_data<T_settings, T_storage_meta_data> | |||
::counter() const | |||
@@ -120,7 +127,7 @@ namespace storage { | |||
grow(grow_size); | |||
} | |||
assert(!_free_ids.empty()); | |||
auto index = _free_ids.back(); | |||
auto index = _free_ids.front(); | |||
auto& item = _container.at(index); | |||
_free_ids.pop(); | |||
return entity_handle(index, item.counter()); | |||
@@ -2,6 +2,7 @@ | |||
#include <ecs/config.h> | |||
#include <ecs/signature/system.h> | |||
#include <ecs/context/defer_proxy.h> | |||
#include <ecs/core/utils/bitset.h> | |||
#include <ecs/core/utils/fixed_function.h> | |||
#include <ecs/core/utils/ordered_vector.h> | |||
@@ -14,7 +15,8 @@ namespace system { | |||
struct deferred_function_vector | |||
{ | |||
private: | |||
using deferred_proxy_type = int; | |||
using settings_type = T_settings; | |||
using deferred_proxy_type = context::detail::defer_proxy_t<settings_type>; | |||
using function_type = utils::fixed_function<void(deferred_proxy_type&)>; | |||
using function_vector_type = std::vector<function_type>; | |||
@@ -92,7 +94,7 @@ namespace system { | |||
public: | |||
instance() | |||
: _bitset () | |||
: _bitset (bitset_type::from_system_signature(mp::unwrap(system_signature_type { }))) | |||
, _system () | |||
, _executor (mp::unwrap(system_signature_type { }).parallelism()()) | |||
, _subscribed() | |||
@@ -149,7 +151,10 @@ namespace system { | |||
inline void execute(T_context& context, T_func&& func) | |||
{ _executor(context, *this, std::forward<T_func>(func)); } | |||
public: /* bitset */ | |||
public: /* misc */ | |||
constexpr decltype(auto) signature() const noexcept | |||
{ return mp::unwrap(system_signature_type { }); } | |||
inline const auto& bitset() const noexcept | |||
{ return _bitset; } | |||
@@ -173,11 +178,22 @@ namespace system { | |||
inline bool is_subscribed(const entity_handle_type& handle) const | |||
{ return (_subscribed.find(handle) != _subscribed.end()); } | |||
inline void subscribe(const entity_handle_type& handle) | |||
{ _subscribed.insert(handle); } | |||
inline bool subscribe(const entity_handle_type& handle) | |||
{ return _subscribed.insert(handle).second; } | |||
inline void unsubscribe(const entity_handle_type& handle) | |||
{ _subscribed.erase(handle); } | |||
inline bool unsubscribe(const entity_handle_type& handle) | |||
{ | |||
auto it = _subscribed.find(handle); | |||
if (it != _subscribed.end()) | |||
{ | |||
_subscribed.erase(it); | |||
return true; | |||
} | |||
else | |||
{ | |||
return false; | |||
} | |||
} | |||
public: /* states */ | |||
template<typename T_func> | |||
@@ -1,5 +1,7 @@ | |||
#pragma once | |||
// #define ECS_DEBUG_ATOMIC_COUNTER | |||
#include <ecs/core/system/scheduler/atomic_counter.h> | |||
namespace ecs { | |||
@@ -106,7 +108,6 @@ namespace scheduler { | |||
inline void task_group_t<T_context, T_dependency_items> | |||
::post_task_in_thread_pool(T_task_id, T_func&& func) | |||
{ | |||
std::cout << "post_task_in_thread_pool (current=" << std::this_thread::get_id() << ", task=" << T_task_id::value << ")" << std::endl; | |||
_context.post_in_thread_pool([this, &func]{ | |||
execute_task(T_task_id { }, func); | |||
}); | |||
@@ -117,7 +118,6 @@ namespace scheduler { | |||
inline void task_group_t<T_context, T_dependency_items> | |||
::execute_task(T_task_id, T_func&& func) | |||
{ | |||
std::cout << "execute_task (current=" << std::this_thread::get_id() << ", task=" << T_task_id::value << ")" << std::endl; | |||
auto& task = hana::at(_tasks, T_task_id { }); | |||
auto& instance = _context.instance_by_tag(task.dependency_item.tag); | |||
instance.execute(_context, func); | |||
@@ -129,7 +129,6 @@ namespace scheduler { | |||
inline void task_group_t<T_context, T_dependency_items> | |||
::check_and_start_dependencies(T_task_id, T_func&& func) | |||
{ | |||
std::cout << "check_and_start_dependencies (current=" << std::this_thread::get_id() << ", task=" << T_task_id::value << ")" << std::endl; | |||
auto& task = hana::at(_tasks, T_task_id { }); | |||
task.for_dependent_ids([this, &func](auto id){ | |||
auto& other = hana::at(_tasks, id); | |||
@@ -250,7 +249,7 @@ namespace scheduler { | |||
using independent_item_ids_type = mp::decay_t<decltype(detail::get_independent_item_ids( | |||
dependency_list_type { }))>;; | |||
/* TODO debug beg! */ | |||
#ifdef ECS_DEBUG_ATOMIC_COUNTER | |||
size_t i = 0; | |||
std::cout << "dependency_list" << std::endl; | |||
hana::for_each(dependency_list_type { }, [&i](auto item){ | |||
@@ -260,7 +259,7 @@ namespace scheduler { | |||
std::cout << " " << hana::value(id) << std::endl; | |||
}); | |||
}); | |||
/* TODO debug end! */ | |||
#endif | |||
using task_group_type = detail::task_group_t<context_type, dependency_list_type>; | |||
@@ -53,14 +53,14 @@ namespace storage { | |||
make_tuple { }))>; | |||
private: | |||
storage_type _storage; | |||
storage_type _storage; | |||
public: | |||
constexpr decltype(auto) size() const noexcept | |||
{ return (ssl_type { }).size(); } | |||
template<typename T_func> | |||
constexpr void for_each(T_func&& f) const noexcept | |||
constexpr void for_each(T_func&& f) noexcept | |||
{ hana::for_each(_storage, std::forward<T_func>(f)); } | |||
template<typename T_system> | |||
@@ -52,7 +52,19 @@ namespace utils { | |||
* @return integral constant with the ID of the requested component tag | |||
*/ | |||
template<typename T_component_tag> | |||
static constexpr decltype(auto) component_id(T_component_tag ct) noexcept; | |||
static constexpr decltype(auto) component_id(T_component_tag&& ct) noexcept; | |||
/** | |||
* get the bitset from the given system signature | |||
* | |||
* @tparam T_system_signature system signature type to get bitmask for | |||
* | |||
* @param ssig system signature to get bitmask for | |||
* | |||
* @return bitmask for the passed system signature | |||
*/ | |||
template<typename T_system_signature> | |||
static constexpr decltype(auto) from_system_signature(T_system_signature&& ssig) noexcept; | |||
/** | |||
* internal bitset type that contains all components | |||
@@ -63,6 +75,13 @@ namespace utils { | |||
bitset_type _bitset; | |||
public: | |||
/** | |||
* return a string representation of the bitset | |||
* | |||
* @return string representation of the bitset | |||
*/ | |||
inline decltype(auto) to_string() const; | |||
/** | |||
* clear all bits of the bitset | |||
*/ | |||
@@ -9,9 +9,28 @@ namespace utils { | |||
template<typename T_settings> | |||
template<typename T_component_tag> | |||
constexpr decltype(auto) bitset<T_settings> | |||
::component_id(T_component_tag ct) noexcept | |||
::component_id(T_component_tag&& ct) noexcept | |||
{ | |||
return mp::list::index_of(all_components(), ct); | |||
return mp::list::index_of(all_components(), std::forward<T_component_tag>(ct)); | |||
} | |||
template<typename T_settings> | |||
template<typename T_system_signature> | |||
constexpr decltype(auto) bitset<T_settings> | |||
::from_system_signature(T_system_signature&& ssig) noexcept | |||
{ | |||
bitset ret; | |||
hana::for_each(hana::concat(ssig.read(), ssig.write()), [&ret](auto ct){ | |||
ret.set_component(ct, true); | |||
}); | |||
return ret; | |||
} | |||
template<typename T_settings> | |||
inline decltype(auto) bitset<T_settings> | |||
::to_string() const | |||
{ | |||
return _bitset.to_string(); | |||
} | |||
template<typename T_settings> | |||
@@ -26,7 +45,7 @@ namespace utils { | |||
inline bool bitset<T_settings> | |||
::contains(const T_other& other) const noexcept | |||
{ | |||
return (_bitset & other._bitset) == _bitset; | |||
return (_bitset & other._bitset) == other._bitset; | |||
} | |||
template<typename T_settings> | |||
@@ -152,7 +152,7 @@ namespace utils { | |||
inline decltype(auto) insert(const value_type& value) | |||
{ | |||
auto it = std::lower_bound(begin(), end(), value); | |||
auto is_new = _compare(value, *it); | |||
auto is_new = (it != end() && _compare(value, *it)); | |||
if (it == end() || is_new) | |||
_items.insert(it, value); | |||
return std::make_pair(it, is_new); | |||
@@ -181,6 +181,9 @@ namespace utils { | |||
template<typename... T_args> | |||
constexpr decltype(auto) erase(T_args&&... args) | |||
{ return _items.erase(std::forward<T_args>(args)...); } | |||
void clear() | |||
{ _items.clear(); } | |||
}; | |||
} } } |
@@ -40,13 +40,9 @@ namespace test | |||
double y; | |||
}; | |||
struct test | |||
{ }; | |||
MAKE_COMPONENT_TAG(position) | |||
MAKE_COMPONENT_TAG(velocity) | |||
MAKE_COMPONENT_TAG(acceleration) | |||
MAKE_COMPONENT_TAG(test) | |||
} | |||
namespace system | |||
@@ -60,13 +56,9 @@ namespace test | |||
struct render | |||
{ }; | |||
struct foo | |||
{ }; | |||
MAKE_SYSTEM_TAG(accelerate) | |||
MAKE_SYSTEM_TAG(move) | |||
MAKE_SYSTEM_TAG(render) | |||
MAKE_SYSTEM_TAG(foo) | |||
} | |||
} | |||
@@ -96,19 +88,10 @@ constexpr decltype(auto) cs_acceleration = | |||
cs::make(ct::acceleration) | |||
.storage(cs::storage::dynamic<storage_size>); | |||
constexpr decltype(auto) cs_test = | |||
cs::make(ct::test) | |||
.storage(cs::storage::dynamic<storage_size>); | |||
constexpr decltype(auto) cs_list = csl::make( | |||
cs_position, | |||
cs_velocity, | |||
cs_acceleration, | |||
cs_test); | |||
constexpr decltype(auto) ss_foo = | |||
ss::make(st::foo) | |||
.write(ct::test); | |||
cs_acceleration); | |||
constexpr decltype(auto) ss_accelerate = | |||
ss::make(st::accelerate) | |||
@@ -122,45 +105,90 @@ constexpr decltype(auto) ss_move = | |||
constexpr decltype(auto) ss_render = | |||
ss::make(st::render) | |||
.read(ct::velocity, ct::position, ct::acceleration, ct::test); | |||
.read(ct::velocity, ct::position, ct::acceleration); | |||
constexpr decltype(auto) ss_list = ssl::make( | |||
ss_foo, | |||
ss_accelerate, | |||
ss_move, | |||
ss_render); | |||
constexpr decltype(auto) settings = ::ecs::settings::make() | |||
.component_signatures(cs_list) | |||
.system_signatures (ss_list); | |||
.system_signatures (ss_list) | |||
.refresh_parallelism (ecs::settings::refresh_parallelism::disable); | |||
namespace sea = ::ecs::system_execution_adapter; | |||
double frand(double min, double max) | |||
{ | |||
double f = (double)rand() / RAND_MAX; | |||
return min + f * (max - min); | |||
} | |||
TEST(DummyTest, fuu) | |||
{ | |||
srand(static_cast<unsigned int>(time(nullptr))); | |||
auto context = ::ecs::context::make(settings); | |||
context.step([](auto& step_proxy){ | |||
step_proxy.execute_systems()( | |||
sea::tags(st::foo) | |||
.for_subtasks([](auto& s, auto& data){ | |||
std::cout << "foo" << std::endl | |||
<< " " << type_helper<decltype(data)>::name() << std::endl; | |||
}), | |||
sea::tags(st::accelerate) | |||
.for_subtasks([](auto& s, auto& data){ | |||
std::cout << "accelerate" << std::endl | |||
<< " " << type_helper<decltype(data)>::name() << std::endl; | |||
}), | |||
sea::tags(st::move) | |||
.for_subtasks([](auto& s, auto& data){ | |||
std::cout << "move" << std::endl | |||
<< " " << type_helper<decltype(data)>::name() << std::endl; | |||
}), | |||
sea::tags(st::render) | |||
.for_subtasks([](auto& s, auto& data){ | |||
std::cout << "render" << std::endl | |||
<< " " << type_helper<decltype(data)>::name() << std::endl; | |||
}) | |||
); | |||
context.step([](auto& proxy){ | |||
for (auto i = 0; i < 10; ++i) { | |||
auto handle = proxy.create_entity(); | |||
auto& position = proxy.add_component(handle, ct::position); | |||
position.x = frand(-100, 100); | |||
position.y = frand(-100, 100); | |||
auto& velocity = proxy.add_component(handle, ct::velocity); | |||
velocity.x = frand(-1, 1); | |||
velocity.y = frand(-1, 1); | |||
auto& acceleration = proxy.add_component(handle, ct::acceleration); | |||
acceleration.x = frand(-1, 1); | |||
acceleration.y = frand(-1, 1); | |||
} | |||
}); | |||
for (int i = 0; i < 10; ++i) { | |||
context.step([](auto& proxy){ | |||
proxy.execute_systems()( | |||
sea::tags(st::accelerate) | |||
.for_subtasks([](auto& s, auto& data){ | |||
data.for_entities([&data](auto& handle) { | |||
auto& velocity = data.get_component(ct::velocity, handle); | |||
auto& acceleration = data.get_component(ct::acceleration, handle); | |||
velocity.x += acceleration.x; | |||
velocity.y += acceleration.y; | |||
}); | |||
}), | |||
sea::tags(st::move) | |||
.for_subtasks([](auto& s, auto& data){ | |||
data.for_entities([&data](auto& handle) { | |||
auto& position = data.get_component(ct::position, handle); | |||
auto& velocity = data.get_component(ct::velocity, handle); | |||
position.x += velocity.x; | |||
position.y += velocity.y; | |||
}); | |||
}), | |||
sea::tags(st::render) | |||
.for_subtasks([](auto& s, auto& data){ | |||
std::cout << "render" << std::endl; | |||
data.for_entities([&data](auto& handle) { | |||
auto& position = data.get_component(ct::position, handle); | |||
auto& velocity = data.get_component(ct::velocity, handle); | |||
auto& acceleration = data.get_component(ct::acceleration, handle); | |||
std::cout << std::fixed | |||
<< " " << handle.index() << ": " | |||
<< "pos(" << position.x << ";" << position.y << ") " | |||
<< "vel(" << velocity.x << ";" << velocity.y << ") " | |||
<< "acc(" << acceleration.x << ";" << acceleration.y << ")" | |||
<< std::endl; | |||
}); | |||
}) | |||
); | |||
}); | |||
} | |||
} |