Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

373 righe
15 KiB

  1. #pragma once
  2. // #define ECS_DEBUG_ATOMIC_COUNTER
  3. #include <ecs/core/system/scheduler/atomic_counter.h>
  4. #include <ecs/core/utils/fixed_function.inl>
  5. namespace ecs {
  6. namespace core {
  7. namespace system {
  8. namespace scheduler {
  9. namespace detail
  10. {
  11. /* misc */
  12. struct make_is_bound_to_main
  13. {
  14. constexpr decltype(auto) operator()() const
  15. {
  16. return hana::is_valid(
  17. [](auto t) -> hana::type<typename decltype(t)::type::bind_to_main> { });
  18. }
  19. };
  20. struct make_is_bound_to_thread
  21. {
  22. constexpr decltype(auto) operator()() const
  23. {
  24. return hana::is_valid(
  25. [](auto t) -> hana::type<typename decltype(t)::type::bind_to_thread> { });
  26. }
  27. };
  28. /* task_t */
  29. template<
  30. typename T_dependency_item,
  31. typename T_dependent_ids>
  32. inline task_t<T_dependency_item, T_dependent_ids>
  33. ::task_t()
  34. : _dependency_count(hana::value(hana::size(mp::unwrap(dependency_item_type { }).dependency_ids)))
  35. { }
  36. template<
  37. typename T_dependency_item,
  38. typename T_dependent_ids>
  39. inline size_t task_t<T_dependency_item, T_dependent_ids>
  40. ::dependency_count() const
  41. {
  42. return _dependency_count;
  43. }
  44. template<
  45. typename T_dependency_item,
  46. typename T_dependent_ids>
  47. inline bool task_t<T_dependency_item, T_dependent_ids>
  48. ::decrement_and_check()
  49. {
  50. return (--_dependency_count == 0);
  51. }
  52. template<
  53. typename T_dependency_item,
  54. typename T_dependent_ids>
  55. template<typename T_func>
  56. constexpr decltype(auto) task_t<T_dependency_item, T_dependent_ids>
  57. ::for_dependent_ids(T_func&& func) const noexcept
  58. {
  59. hana::for_each(dependent_ids_type { }, std::forward<T_func>(func));
  60. }
  61. /* dependent_ids_t */
  62. template<typename T_dependency_items, typename T_dependency_item>
  63. constexpr decltype(auto) dependent_ids_t
  64. ::operator()(T_dependency_items, T_dependency_item) const noexcept
  65. {
  66. auto item_id = mp::list::index_of(T_dependency_items { }, T_dependency_item { });
  67. return hana::fold_right(T_dependency_items { }, mp::list::empty, [=](auto other, auto acc){
  68. return hana::if_(
  69. hana::contains(mp::unwrap(other).dependency_ids, item_id),
  70. hana::append(acc, mp::list::index_of(T_dependency_items { }, other)),
  71. acc);
  72. });
  73. }
  74. /* make_tasks_t */
  75. template<typename T_dependency_items>
  76. constexpr decltype(auto) make_tasks_t
  77. ::operator()(T_dependency_items) const noexcept
  78. {
  79. return hana::transform(T_dependency_items { }, [](auto item){
  80. using item_type = mp::decay_t<decltype(item)>;
  81. using dependent_ids_type = mp::decay_t<decltype((dependent_ids_t { })(T_dependency_items { }, item_type { }))>;
  82. using task_type = task_t<item_type, dependent_ids_type>;
  83. return hana::type_c<task_type>;
  84. });
  85. }
  86. /* task_counter_blocker_t */
  87. template<typename T_task>
  88. inline void task_counter_blocker_t
  89. ::post(T_task&& task)
  90. {
  91. std::lock_guard lock(_mutex);
  92. _tasks.emplace(std::forward<T_task>(task));
  93. _cond_var.notify_all();
  94. }
  95. inline void task_counter_blocker_t
  96. ::process()
  97. {
  98. while(true)
  99. {
  100. utils::task t;
  101. {
  102. std::lock_guard lock(_mutex);
  103. if (_tasks.empty())
  104. {
  105. return;
  106. }
  107. t = std::move(_tasks.front());
  108. _tasks.pop();
  109. try
  110. {
  111. t(static_cast<size_t>(-1));
  112. }
  113. catch(const std::exception& ex)
  114. {
  115. std::cerr << "error in main thread: " << ex.what() << std::endl;
  116. }
  117. catch(...)
  118. {
  119. std::cerr << "error in main thread: unknown" << std::endl;
  120. }
  121. }
  122. }
  123. }
  124. /* task_group_t */
  125. template<typename T_context, typename T_dependency_items>
  126. inline task_group_t<T_context, T_dependency_items>
  127. ::task_group_t(context_type& p_context, task_counter_blocker_t& p_counter)
  128. : _context(p_context)
  129. , _counter(p_counter)
  130. { }
  131. template<typename T_context, typename T_dependency_items>
  132. template<typename T_task_id, typename T_func>
  133. inline void task_group_t<T_context, T_dependency_items>
  134. ::start_task(T_task_id id, T_func&& func)
  135. {
  136. using task_type = mp::decay_t<decltype(hana::at(_tasks, id))>;
  137. using dependency_item_type = typename task_type::dependency_item_type;
  138. _counter.increment();
  139. hana::eval_if(
  140. mp::unwrap(dependency_item_type { }).execute,
  141. [this, &func](auto _) { _(this)->post_task_in_thread_pool (T_task_id { }, func); },
  142. [this, &func](auto _) { _(this)->check_and_start_dependencies(T_task_id { }, func); });
  143. }
  144. template<typename T_context, typename T_dependency_items>
  145. template<typename T_task_id, typename T_func>
  146. inline void task_group_t<T_context, T_dependency_items>
  147. ::post_task_in_thread_pool(T_task_id, T_func&& func)
  148. {
  149. using task_type = mp::decay_t<decltype(hana::at(_tasks, T_task_id { }))>;
  150. using instance_type = mp::decay_t<decltype(_context.instance_by_tag(std::declval<task_type>().dependency_item.tag))>;
  151. using signature_type = typename instance_type::system_signature_type;
  152. using executor_builder_type = mp::decay_t<decltype(mp::unwrap(signature_type { }).parallelism())>;
  153. using is_bound_to_main_type = mp::decay_t<decltype(make_is_bound_to_main { } ())>;
  154. using is_bound_to_thread_type = mp::decay_t<decltype(make_is_bound_to_thread { } ())>;
  155. hana::eval_if(
  156. is_bound_to_main_type{ }(hana::type_c<executor_builder_type>),
  157. [this, &func](auto _){
  158. _counter.post([this, &func](size_t thread_id){
  159. execute_task(T_task_id { }, func);
  160. });
  161. },
  162. [this, &func](auto _){
  163. hana::eval_if(is_bound_to_thread_type{ }(hana::type_c<executor_builder_type>),
  164. [this, &func](auto _){
  165. auto& meta = _context.instance_by_tag(task_type { }.dependency_item.tag).scheduler_meta_data();
  166. _context.post_in_thread_pool([this, &func, &meta](size_t thread_id){
  167. meta.thread_id = static_cast<ssize_t>(thread_id);
  168. execute_task(T_task_id { }, func);
  169. }, meta.thread_id);
  170. },
  171. [this, &func](auto _){
  172. _context.post_in_thread_pool([this, &func](size_t thread_id){
  173. execute_task(T_task_id { }, func);
  174. });
  175. });
  176. });
  177. }
  178. template<typename T_context, typename T_dependency_items>
  179. template<typename T_task_id, typename T_func>
  180. inline void task_group_t<T_context, T_dependency_items>
  181. ::execute_task(T_task_id, T_func&& func)
  182. {
  183. auto& task = hana::at(_tasks, T_task_id { });
  184. auto& instance = _context.instance_by_tag(task.dependency_item.tag);
  185. instance.execute(_context, func);
  186. check_and_start_dependencies(T_task_id { }, func);
  187. }
  188. template<typename T_context, typename T_dependency_items>
  189. template<typename T_task_id, typename T_func>
  190. inline void task_group_t<T_context, T_dependency_items>
  191. ::check_and_start_dependencies(T_task_id, T_func&& func)
  192. {
  193. auto& task = hana::at(_tasks, T_task_id { });
  194. task.for_dependent_ids([this, &func](auto id){
  195. auto& other = hana::at(_tasks, id);
  196. if (other.decrement_and_check()) {
  197. start_task(id, func);
  198. }
  199. });
  200. _counter.decrement();
  201. }
  202. /* misc */
  203. template<typename T_system_signature_list, typename T_system_tag_list>
  204. constexpr decltype(auto) build_dependency_list(T_system_signature_list, T_system_tag_list) noexcept
  205. {
  206. using system_signature_list_type = T_system_signature_list;
  207. using system_signature_tuple_type = mp::decay_t<decltype((system_signature_list_type { }).items)>;
  208. using system_tag_list_type = T_system_tag_list;
  209. /* filter system signature list by the passed system tags */
  210. auto filtered = (system_signature_list_type { })
  211. .filter([](auto ssig){
  212. return hana::contains(system_tag_list_type { }, mp::unwrap(ssig).tag());
  213. });
  214. using filtered_ssig_list_type = mp::decay_t<decltype(filtered)>;
  215. /* check if any systems that does not depend on each other, wants to get write access to a component */
  216. (filtered_ssig_list_type { }).for_each([](auto ssig){
  217. using ssig_type = mp::decay_t<decltype(ssig)>;
  218. hana::for_each(mp::unwrap(ssig).write(), [](auto tag){
  219. using tag_type = mp::decay_t<decltype(tag)>;
  220. (filtered_ssig_list_type { }).for_each([](auto other){
  221. using check_dependencies_type = decltype(
  222. hana::or_(
  223. hana::equal(ssig_type { }, other),
  224. (T_system_signature_list { }).depends_on(ssig_type { }, mp::unwrap(other).tag()),
  225. (T_system_signature_list { }).depends_on(other, mp::unwrap(ssig_type { }).tag()),
  226. hana::not_(hana::contains(mp::unwrap(other).write(), tag_type { }))));
  227. static_assert(
  228. check_dependencies_type { },
  229. "independent systems can not share write access to component");
  230. });
  231. });
  232. });
  233. /* build the actual dependency item list */
  234. return hana::transform(system_signature_tuple_type { }, [](auto ssig){
  235. using ssig_type = mp::decay_t<decltype(ssig)>;
  236. auto tag = mp::unwrap(ssig).tag();
  237. auto deps = mp::unwrap(ssig).dependencies();
  238. auto execute = hana::contains(system_tag_list_type { }, mp::unwrap(ssig).tag());
  239. auto read_deps =
  240. hana::transform(mp::unwrap(ssig).read(), [](auto read_tag){
  241. using read_tag_type = mp::decay_t<decltype(read_tag)>;
  242. return hana::transform(
  243. hana::filter((filtered_ssig_list_type { }).items, [](auto other){
  244. return hana::and_(
  245. hana::not_equal(ssig_type { }, other),
  246. hana::contains(mp::unwrap(other).write(), read_tag_type { }));
  247. }),
  248. [](auto other){
  249. return mp::unwrap(other).tag();
  250. });
  251. });
  252. auto final_deps =
  253. hana::transform(
  254. hana::fold(
  255. hana::if_(
  256. hana::size(read_deps) == hana::size_c<0>,
  257. hana::make_basic_tuple(hana::make_basic_tuple()),
  258. read_deps),
  259. deps,
  260. hana::concat),
  261. [](auto tmp_tag){
  262. return (system_signature_list_type { }).id_by_tag(tmp_tag);
  263. });
  264. using tag_type = mp::decay_t<decltype(tag)>;
  265. using dep_type = mp::decay_t<decltype(final_deps)>;
  266. using execute_type = mp::decay_t<decltype(execute)>;
  267. using item_type = dependency_item_t<tag_type, execute_type, dep_type>;
  268. return hana::type_c<item_type>;
  269. });
  270. }
  271. template<typename T_dependency_list>
  272. constexpr decltype(auto) get_independent_item_ids(T_dependency_list) noexcept
  273. {
  274. return
  275. hana::transform(
  276. hana::filter(T_dependency_list { }, [](auto item){
  277. return hana::size(mp::unwrap(item).dependency_ids) == hana::size_c<0>;
  278. }),
  279. [](auto item){
  280. return mp::list::index_of(T_dependency_list { }, item);
  281. });
  282. }
  283. }
  284. /* atomic_counter */
  285. template<typename T_settings>
  286. inline atomic_counter<T_settings>
  287. ::atomic_counter(context_type& p_context)
  288. : _context(p_context)
  289. { }
  290. template<typename T_settings>
  291. template<typename T_system_tag_list, typename T_func>
  292. inline void atomic_counter<T_settings>
  293. ::execute(T_system_tag_list stl, T_func&& func)
  294. {
  295. static_assert(tag::system::is_list(T_system_tag_list { }));
  296. using dependency_list_type = mp::decay_t<decltype(detail::build_dependency_list(
  297. (settings_type { }).system_signatures(),
  298. T_system_tag_list { }))>;
  299. using independent_item_ids_type = mp::decay_t<decltype(detail::get_independent_item_ids(
  300. dependency_list_type { }))>;;
  301. #ifdef ECS_DEBUG_ATOMIC_COUNTER
  302. size_t i = 0;
  303. std::cout << "dependency_list" << std::endl;
  304. hana::for_each(dependency_list_type { }, [&i](auto item){
  305. using system_type = mp::unwrap_t<mp::decay_t<decltype(mp::unwrap(item).tag)>>;
  306. std::cout << " " << (i++) << ": " << type_helper<system_type>::name() << " (" << hana::value(mp::unwrap(item).execute) << ")" << std::endl;
  307. hana::for_each(mp::unwrap(item).dependency_ids, [](auto id){
  308. std::cout << " " << hana::value(id) << std::endl;
  309. });
  310. });
  311. #endif
  312. using task_group_type = detail::task_group_t<context_type, dependency_list_type>;
  313. detail::task_counter_blocker_t counter (1);
  314. task_group_type task_group (_context, counter);
  315. counter.execute_and_wait_tick(
  316. [&task_group, &counter, &func]{
  317. hana::for_each(independent_item_ids_type { }, [&task_group, &func](auto id){
  318. task_group.start_task(id, func);
  319. });
  320. counter.decrement();
  321. },
  322. [&counter]{
  323. counter.process();
  324. });
  325. }
  326. } } } }