🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

484 lines
19 KiB

  1. #ifndef ENTT_ENTITY_ORGANIZER_HPP
  2. #define ENTT_ENTITY_ORGANIZER_HPP
  3. #include <algorithm>
  4. #include <cstddef>
  5. #include <type_traits>
  6. #include <utility>
  7. #include <vector>
  8. #include "../container/dense_map.hpp"
  9. #include "../core/type_info.hpp"
  10. #include "../core/type_traits.hpp"
  11. #include "../core/utility.hpp"
  12. #include "fwd.hpp"
  13. #include "helper.hpp"
  14. namespace entt {
  15. /**
  16. * @cond TURN_OFF_DOXYGEN
  17. * Internal details not to be documented.
  18. */
  19. namespace internal {
  20. template<typename>
  21. struct is_view: std::false_type {};
  22. template<typename Entity, typename... Component, typename... Exclude>
  23. struct is_view<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>>: std::true_type {};
  24. template<typename Type>
  25. inline constexpr bool is_view_v = is_view<Type>::value;
  26. template<typename Type, typename Override>
  27. struct unpack_type {
  28. using ro = std::conditional_t<
  29. type_list_contains_v<Override, std::add_const_t<Type>> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
  30. type_list<std::remove_const_t<Type>>,
  31. type_list<>>;
  32. using rw = std::conditional_t<
  33. type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, std::add_const_t<Type>>),
  34. type_list<Type>,
  35. type_list<>>;
  36. };
  37. template<typename Entity, typename... Override>
  38. struct unpack_type<basic_registry<Entity>, type_list<Override...>> {
  39. using ro = type_list<>;
  40. using rw = type_list<>;
  41. };
  42. template<typename Entity, typename... Override>
  43. struct unpack_type<const basic_registry<Entity>, type_list<Override...>>
  44. : unpack_type<basic_registry<Entity>, type_list<Override...>> {};
  45. template<typename Entity, typename... Component, typename... Exclude, typename... Override>
  46. struct unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {
  47. using ro = type_list_cat_t<type_list<Exclude...>, typename unpack_type<Component, type_list<Override...>>::ro...>;
  48. using rw = type_list_cat_t<typename unpack_type<Component, type_list<Override...>>::rw...>;
  49. };
  50. template<typename Entity, typename... Component, typename... Exclude, typename... Override>
  51. struct unpack_type<const basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>>
  52. : unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {};
  53. template<typename, typename>
  54. struct resource_traits;
  55. template<typename... Args, typename... Req>
  56. struct resource_traits<type_list<Args...>, type_list<Req...>> {
  57. using args = type_list<std::remove_const_t<Args>...>;
  58. using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>;
  59. using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>;
  60. };
  61. template<typename... Req, typename Ret, typename... Args>
  62. resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
  63. template<typename... Req, typename Ret, typename Type, typename... Args>
  64. resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
  65. template<typename... Req, typename Ret, typename Class, typename... Args>
  66. resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
  67. template<typename... Req, typename Ret, typename Class, typename... Args>
  68. resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
  69. } // namespace internal
  70. /**
  71. * Internal details not to be documented.
  72. * @endcond
  73. */
  74. /**
  75. * @brief Utility class for creating a static task graph.
  76. *
  77. * This class offers minimal support (but sufficient in many cases) for creating
  78. * an execution graph from functions and their requirements on resources.<br/>
  79. * Note that the resulting tasks aren't executed in any case. This isn't the
  80. * goal of the tool. Instead, they are returned to the user in the form of a
  81. * graph that allows for safe execution.
  82. *
  83. * @tparam Entity A valid entity type (see entt_traits for more details).
  84. */
  85. template<typename Entity>
  86. class basic_organizer final {
  87. using callback_type = void(const void *, basic_registry<Entity> &);
  88. using prepare_type = void(basic_registry<Entity> &);
  89. using dependency_type = std::size_t(const bool, const type_info **, const std::size_t);
  90. struct vertex_data final {
  91. std::size_t ro_count{};
  92. std::size_t rw_count{};
  93. const char *name{};
  94. const void *payload{};
  95. callback_type *callback{};
  96. dependency_type *dependency;
  97. prepare_type *prepare{};
  98. const type_info *info{};
  99. };
  100. template<typename Type>
  101. [[nodiscard]] static decltype(auto) extract(basic_registry<Entity> &reg) {
  102. if constexpr(std::is_same_v<Type, basic_registry<Entity>>) {
  103. return reg;
  104. } else if constexpr(internal::is_view_v<Type>) {
  105. return as_view{reg};
  106. } else {
  107. return reg.ctx().template emplace<std::remove_reference_t<Type>>();
  108. }
  109. }
  110. template<typename... Args>
  111. [[nodiscard]] static auto to_args(basic_registry<Entity> &reg, type_list<Args...>) {
  112. return std::tuple<decltype(extract<Args>(reg))...>(extract<Args>(reg)...);
  113. }
  114. template<typename... Type>
  115. static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
  116. if constexpr(sizeof...(Type) == 0u) {
  117. return {};
  118. } else {
  119. const type_info *info[sizeof...(Type)]{&type_id<Type>()...};
  120. const auto length = (std::min)(count, sizeof...(Type));
  121. std::copy_n(info, length, buffer);
  122. return length;
  123. }
  124. }
  125. template<typename... RO, typename... RW>
  126. void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) {
  127. dependencies[type_hash<basic_registry<Entity>>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
  128. (dependencies[type_hash<RO>::value()].emplace_back(index, false), ...);
  129. (dependencies[type_hash<RW>::value()].emplace_back(index, true), ...);
  130. }
  131. [[nodiscard]] std::vector<bool> adjacency_matrix() {
  132. const auto length = vertices.size();
  133. std::vector<bool> edges(length * length, false);
  134. // creates the adjacency matrix
  135. for(const auto &deps: dependencies) {
  136. const auto last = deps.second.cend();
  137. auto it = deps.second.cbegin();
  138. while(it != last) {
  139. if(it->second) {
  140. // rw item
  141. if(auto curr = it++; it != last) {
  142. if(it->second) {
  143. edges[curr->first * length + it->first] = true;
  144. } else {
  145. if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
  146. for(; it != next; ++it) {
  147. edges[curr->first * length + it->first] = true;
  148. edges[it->first * length + next->first] = true;
  149. }
  150. } else {
  151. for(; it != next; ++it) {
  152. edges[curr->first * length + it->first] = true;
  153. }
  154. }
  155. }
  156. }
  157. } else {
  158. // ro item, possibly only on first iteration
  159. if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
  160. for(; it != next; ++it) {
  161. edges[it->first * length + next->first] = true;
  162. }
  163. } else {
  164. it = last;
  165. }
  166. }
  167. }
  168. }
  169. // computes the transitive closure
  170. for(std::size_t vk{}; vk < length; ++vk) {
  171. for(std::size_t vi{}; vi < length; ++vi) {
  172. for(std::size_t vj{}; vj < length; ++vj) {
  173. edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]);
  174. }
  175. }
  176. }
  177. // applies the transitive reduction
  178. for(std::size_t vert{}; vert < length; ++vert) {
  179. edges[vert * length + vert] = false;
  180. }
  181. for(std::size_t vj{}; vj < length; ++vj) {
  182. for(std::size_t vi{}; vi < length; ++vi) {
  183. if(edges[vi * length + vj]) {
  184. for(std::size_t vk{}; vk < length; ++vk) {
  185. if(edges[vj * length + vk]) {
  186. edges[vi * length + vk] = false;
  187. }
  188. }
  189. }
  190. }
  191. }
  192. return edges;
  193. }
  194. public:
  195. /*! @brief Underlying entity identifier. */
  196. using entity_type = Entity;
  197. /*! @brief Unsigned integer type. */
  198. using size_type = std::size_t;
  199. /*! @brief Raw task function type. */
  200. using function_type = callback_type;
  201. /*! @brief Vertex type of a task graph defined as an adjacency list. */
  202. struct vertex {
  203. /**
  204. * @brief Constructs a vertex of the task graph.
  205. * @param vtype True if the vertex is a top-level one, false otherwise.
  206. * @param data The data associated with the vertex.
  207. * @param edges The indices of the children in the adjacency list.
  208. */
  209. vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges)
  210. : is_top_level{vtype},
  211. node{std::move(data)},
  212. reachable{std::move(edges)} {}
  213. /**
  214. * @brief Fills a buffer with the type info objects for the writable
  215. * resources of a vertex.
  216. * @param buffer A buffer pre-allocated by the user.
  217. * @param length The length of the user-supplied buffer.
  218. * @return The number of type info objects written to the buffer.
  219. */
  220. size_type ro_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
  221. return node.dependency(false, buffer, length);
  222. }
  223. /**
  224. * @brief Fills a buffer with the type info objects for the read-only
  225. * resources of a vertex.
  226. * @param buffer A buffer pre-allocated by the user.
  227. * @param length The length of the user-supplied buffer.
  228. * @return The number of type info objects written to the buffer.
  229. */
  230. size_type rw_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
  231. return node.dependency(true, buffer, length);
  232. }
  233. /**
  234. * @brief Returns the number of read-only resources of a vertex.
  235. * @return The number of read-only resources of the vertex.
  236. */
  237. size_type ro_count() const ENTT_NOEXCEPT {
  238. return node.ro_count;
  239. }
  240. /**
  241. * @brief Returns the number of writable resources of a vertex.
  242. * @return The number of writable resources of the vertex.
  243. */
  244. size_type rw_count() const ENTT_NOEXCEPT {
  245. return node.rw_count;
  246. }
  247. /**
  248. * @brief Checks if a vertex is also a top-level one.
  249. * @return True if the vertex is a top-level one, false otherwise.
  250. */
  251. bool top_level() const ENTT_NOEXCEPT {
  252. return is_top_level;
  253. }
  254. /**
  255. * @brief Returns a type info object associated with a vertex.
  256. * @return A properly initialized type info object.
  257. */
  258. const type_info &info() const ENTT_NOEXCEPT {
  259. return *node.info;
  260. }
  261. /**
  262. * @brief Returns a user defined name associated with a vertex, if any.
  263. * @return The user defined name associated with the vertex, if any.
  264. */
  265. const char *name() const ENTT_NOEXCEPT {
  266. return node.name;
  267. }
  268. /**
  269. * @brief Returns the function associated with a vertex.
  270. * @return The function associated with the vertex.
  271. */
  272. function_type *callback() const ENTT_NOEXCEPT {
  273. return node.callback;
  274. }
  275. /**
  276. * @brief Returns the payload associated with a vertex, if any.
  277. * @return The payload associated with the vertex, if any.
  278. */
  279. const void *data() const ENTT_NOEXCEPT {
  280. return node.payload;
  281. }
  282. /**
  283. * @brief Returns the list of nodes reachable from a given vertex.
  284. * @return The list of nodes reachable from the vertex.
  285. */
  286. const std::vector<std::size_t> &children() const ENTT_NOEXCEPT {
  287. return reachable;
  288. }
  289. /**
  290. * @brief Prepares a registry and assures that all required resources
  291. * are properly instantiated before using them.
  292. * @param reg A valid registry.
  293. */
  294. void prepare(basic_registry<entity_type> &reg) const {
  295. node.prepare ? node.prepare(reg) : void();
  296. }
  297. private:
  298. bool is_top_level;
  299. vertex_data node;
  300. std::vector<std::size_t> reachable;
  301. };
  302. /**
  303. * @brief Adds a free function to the task list.
  304. * @tparam Candidate Function to add to the task list.
  305. * @tparam Req Additional requirements and/or override resource access mode.
  306. * @param name Optional name to associate with the task.
  307. */
  308. template<auto Candidate, typename... Req>
  309. void emplace(const char *name = nullptr) {
  310. using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate));
  311. constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
  312. callback_type *callback = +[](const void *, basic_registry<entity_type> &reg) {
  313. std::apply(Candidate, to_args(reg, typename resource_type::args{}));
  314. };
  315. vertex_data vdata{
  316. resource_type::ro::size,
  317. resource_type::rw::size,
  318. name,
  319. nullptr,
  320. callback,
  321. +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
  322. +[](basic_registry<entity_type> &reg) { void(to_args(reg, typename resource_type::args{})); },
  323. &type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
  324. track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
  325. vertices.push_back(std::move(vdata));
  326. }
  327. /**
  328. * @brief Adds a free function with payload or a member function with an
  329. * instance to the task list.
  330. * @tparam Candidate Function or member to add to the task list.
  331. * @tparam Req Additional requirements and/or override resource access mode.
  332. * @tparam Type Type of class or type of payload.
  333. * @param value_or_instance A valid object that fits the purpose.
  334. * @param name Optional name to associate with the task.
  335. */
  336. template<auto Candidate, typename... Req, typename Type>
  337. void emplace(Type &value_or_instance, const char *name = nullptr) {
  338. using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate));
  339. constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
  340. callback_type *callback = +[](const void *payload, basic_registry<entity_type> &reg) {
  341. Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
  342. std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{})));
  343. };
  344. vertex_data vdata{
  345. resource_type::ro::size,
  346. resource_type::rw::size,
  347. name,
  348. &value_or_instance,
  349. callback,
  350. +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
  351. +[](basic_registry<entity_type> &reg) { void(to_args(reg, typename resource_type::args{})); },
  352. &type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
  353. track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
  354. vertices.push_back(std::move(vdata));
  355. }
  356. /**
  357. * @brief Adds an user defined function with optional payload to the task
  358. * list.
  359. * @tparam Req Additional requirements and/or override resource access mode.
  360. * @param func Function to add to the task list.
  361. * @param payload User defined arbitrary data.
  362. * @param name Optional name to associate with the task.
  363. */
  364. template<typename... Req>
  365. void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
  366. using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>;
  367. track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
  368. vertex_data vdata{
  369. resource_type::ro::size,
  370. resource_type::rw::size,
  371. name,
  372. payload,
  373. func,
  374. +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
  375. nullptr,
  376. &type_id<void>()};
  377. vertices.push_back(std::move(vdata));
  378. }
  379. /**
  380. * @brief Generates a task graph for the current content.
  381. * @return The adjacency list of the task graph.
  382. */
  383. std::vector<vertex> graph() {
  384. const auto edges = adjacency_matrix();
  385. // creates the adjacency list
  386. std::vector<vertex> adjacency_list{};
  387. adjacency_list.reserve(vertices.size());
  388. for(std::size_t col{}, length = vertices.size(); col < length; ++col) {
  389. std::vector<std::size_t> reachable{};
  390. const auto row = col * length;
  391. bool is_top_level = true;
  392. for(std::size_t next{}; next < length; ++next) {
  393. if(edges[row + next]) {
  394. reachable.push_back(next);
  395. }
  396. }
  397. for(std::size_t next{}; next < length && is_top_level; ++next) {
  398. is_top_level = !edges[next * length + col];
  399. }
  400. adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable));
  401. }
  402. return adjacency_list;
  403. }
  404. /*! @brief Erases all elements from a container. */
  405. void clear() {
  406. dependencies.clear();
  407. vertices.clear();
  408. }
  409. private:
  410. dense_map<id_type, std::vector<std::pair<std::size_t, bool>>, identity> dependencies;
  411. std::vector<vertex_data> vertices;
  412. };
  413. } // namespace entt
  414. #endif