organizer.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. #ifndef ENTT_ENTITY_ORGANIZER_HPP
  2. #define ENTT_ENTITY_ORGANIZER_HPP
  3. #include <cstddef>
  4. #include <type_traits>
  5. #include <utility>
  6. #include <vector>
  7. #include "../core/type_info.hpp"
  8. #include "../core/type_traits.hpp"
  9. #include "../core/utility.hpp"
  10. #include "../graph/adjacency_matrix.hpp"
  11. #include "../graph/flow.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... Args>
  23. struct is_view<basic_view<Args...>>: 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, const 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, const Type>),
  34. type_list<Type>,
  35. type_list<>>;
  36. };
  37. template<typename... Args, typename... Override>
  38. struct unpack_type<basic_registry<Args...>, type_list<Override...>> {
  39. using ro = type_list<>;
  40. using rw = type_list<>;
  41. };
  42. template<typename... Args, typename... Override>
  43. struct unpack_type<const basic_registry<Args...>, type_list<Override...>>
  44. : unpack_type<basic_registry<Args...>, type_list<Override...>> {};
  45. template<typename... Get, typename... Exclude, typename... Override>
  46. struct unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
  47. using ro = type_list_cat_t<type_list<typename Exclude::value_type...>, typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::ro...>;
  48. using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::rw...>;
  49. };
  50. template<typename... Get, typename... Exclude, typename... Override>
  51. struct unpack_type<const basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>>
  52. : unpack_type<basic_view<get_t<Get...>, 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 Registry Basic registry type.
  84. */
  85. template<typename Registry>
  86. class basic_organizer final {
  87. using callback_type = void(const void *, Registry &);
  88. using prepare_type = void(Registry &);
  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(Registry &reg) {
  102. if constexpr(std::is_same_v<Type, Registry>) {
  103. return reg;
  104. } else if constexpr(internal::is_view_v<Type>) {
  105. return static_cast<Type>(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(Registry &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 = count < sizeof...(Type) ? count : sizeof...(Type);
  121. for(std::size_t pos{}; pos < length; ++pos) {
  122. buffer[pos] = info[pos];
  123. }
  124. return length;
  125. }
  126. }
  127. template<typename... RO, typename... RW>
  128. void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) {
  129. builder.bind(static_cast<id_type>(index));
  130. builder.set(type_hash<Registry>::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
  131. (builder.ro(type_hash<RO>::value()), ...);
  132. (builder.rw(type_hash<RW>::value()), ...);
  133. }
  134. public:
  135. /*! Basic registry type. */
  136. using registry_type = Registry;
  137. /*! @brief Underlying entity identifier. */
  138. using entity_type = typename registry_type::entity_type;
  139. /*! @brief Unsigned integer type. */
  140. using size_type = std::size_t;
  141. /*! @brief Raw task function type. */
  142. using function_type = callback_type;
  143. /*! @brief Vertex type of a task graph defined as an adjacency list. */
  144. struct vertex {
  145. /**
  146. * @brief Constructs a vertex of the task graph.
  147. * @param vtype True if the vertex is a top-level one, false otherwise.
  148. * @param data The data associated with the vertex.
  149. * @param edges The indices of the children in the adjacency list.
  150. */
  151. vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges)
  152. : is_top_level{vtype},
  153. node{std::move(data)},
  154. reachable{std::move(edges)} {}
  155. /**
  156. * @brief Fills a buffer with the type info objects for the writable
  157. * resources of a vertex.
  158. * @param buffer A buffer pre-allocated by the user.
  159. * @param length The length of the user-supplied buffer.
  160. * @return The number of type info objects written to the buffer.
  161. */
  162. size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
  163. return node.dependency(false, buffer, length);
  164. }
  165. /**
  166. * @brief Fills a buffer with the type info objects for the read-only
  167. * resources of a vertex.
  168. * @param buffer A buffer pre-allocated by the user.
  169. * @param length The length of the user-supplied buffer.
  170. * @return The number of type info objects written to the buffer.
  171. */
  172. size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
  173. return node.dependency(true, buffer, length);
  174. }
  175. /**
  176. * @brief Returns the number of read-only resources of a vertex.
  177. * @return The number of read-only resources of the vertex.
  178. */
  179. size_type ro_count() const noexcept {
  180. return node.ro_count;
  181. }
  182. /**
  183. * @brief Returns the number of writable resources of a vertex.
  184. * @return The number of writable resources of the vertex.
  185. */
  186. size_type rw_count() const noexcept {
  187. return node.rw_count;
  188. }
  189. /**
  190. * @brief Checks if a vertex is also a top-level one.
  191. * @return True if the vertex is a top-level one, false otherwise.
  192. */
  193. bool top_level() const noexcept {
  194. return is_top_level;
  195. }
  196. /**
  197. * @brief Returns a type info object associated with a vertex.
  198. * @return A properly initialized type info object.
  199. */
  200. const type_info &info() const noexcept {
  201. return *node.info;
  202. }
  203. /**
  204. * @brief Returns a user defined name associated with a vertex, if any.
  205. * @return The user defined name associated with the vertex, if any.
  206. */
  207. const char *name() const noexcept {
  208. return node.name;
  209. }
  210. /**
  211. * @brief Returns the function associated with a vertex.
  212. * @return The function associated with the vertex.
  213. */
  214. function_type *callback() const noexcept {
  215. return node.callback;
  216. }
  217. /**
  218. * @brief Returns the payload associated with a vertex, if any.
  219. * @return The payload associated with the vertex, if any.
  220. */
  221. const void *data() const noexcept {
  222. return node.payload;
  223. }
  224. /**
  225. * @brief Returns the list of nodes reachable from a given vertex.
  226. * @return The list of nodes reachable from the vertex.
  227. */
  228. const std::vector<std::size_t> &children() const noexcept {
  229. return reachable;
  230. }
  231. /**
  232. * @brief Prepares a registry and assures that all required resources
  233. * are properly instantiated before using them.
  234. * @param reg A valid registry.
  235. */
  236. void prepare(registry_type &reg) const {
  237. node.prepare ? node.prepare(reg) : void();
  238. }
  239. private:
  240. bool is_top_level;
  241. vertex_data node;
  242. std::vector<std::size_t> reachable;
  243. };
  244. /**
  245. * @brief Adds a free function to the task list.
  246. * @tparam Candidate Function to add to the task list.
  247. * @tparam Req Additional requirements and/or override resource access mode.
  248. * @param name Optional name to associate with the task.
  249. */
  250. template<auto Candidate, typename... Req>
  251. void emplace(const char *name = nullptr) {
  252. using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate));
  253. constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
  254. callback_type *callback = +[](const void *, registry_type &reg) {
  255. std::apply(Candidate, to_args(reg, typename resource_type::args{}));
  256. };
  257. vertex_data vdata{
  258. resource_type::ro::size,
  259. resource_type::rw::size,
  260. name,
  261. nullptr,
  262. callback,
  263. +[](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); },
  264. +[](registry_type &reg) { void(to_args(reg, typename resource_type::args{})); },
  265. &type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
  266. track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
  267. vertices.push_back(std::move(vdata));
  268. }
  269. /**
  270. * @brief Adds a free function with payload or a member function with an
  271. * instance to the task list.
  272. * @tparam Candidate Function or member to add to the task list.
  273. * @tparam Req Additional requirements and/or override resource access mode.
  274. * @tparam Type Type of class or type of payload.
  275. * @param value_or_instance A valid object that fits the purpose.
  276. * @param name Optional name to associate with the task.
  277. */
  278. template<auto Candidate, typename... Req, typename Type>
  279. void emplace(Type &value_or_instance, const char *name = nullptr) {
  280. using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate));
  281. constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, registry_type>;
  282. callback_type *callback = +[](const void *payload, registry_type &reg) {
  283. Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
  284. std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{})));
  285. };
  286. vertex_data vdata{
  287. resource_type::ro::size,
  288. resource_type::rw::size,
  289. name,
  290. &value_or_instance,
  291. callback,
  292. +[](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); },
  293. +[](registry_type &reg) { void(to_args(reg, typename resource_type::args{})); },
  294. &type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
  295. track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
  296. vertices.push_back(std::move(vdata));
  297. }
  298. /**
  299. * @brief Adds an user defined function with optional payload to the task
  300. * list.
  301. * @tparam Req Additional requirements and/or override resource access mode.
  302. * @param func Function to add to the task list.
  303. * @param payload User defined arbitrary data.
  304. * @param name Optional name to associate with the task.
  305. */
  306. template<typename... Req>
  307. void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
  308. using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>;
  309. track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
  310. vertex_data vdata{
  311. resource_type::ro::size,
  312. resource_type::rw::size,
  313. name,
  314. payload,
  315. func,
  316. +[](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); },
  317. nullptr,
  318. &type_id<void>()};
  319. vertices.push_back(std::move(vdata));
  320. }
  321. /**
  322. * @brief Generates a task graph for the current content.
  323. * @return The adjacency list of the task graph.
  324. */
  325. std::vector<vertex> graph() {
  326. std::vector<vertex> adjacency_list{};
  327. adjacency_list.reserve(vertices.size());
  328. auto adjacency_matrix = builder.graph();
  329. for(auto curr: adjacency_matrix.vertices()) {
  330. const auto iterable = adjacency_matrix.in_edges(curr);
  331. std::vector<std::size_t> reachable{};
  332. for(auto &&edge: adjacency_matrix.out_edges(curr)) {
  333. reachable.push_back(edge.second);
  334. }
  335. adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable));
  336. }
  337. return adjacency_list;
  338. }
  339. /*! @brief Erases all elements from a container. */
  340. void clear() {
  341. builder.clear();
  342. vertices.clear();
  343. }
  344. private:
  345. std::vector<vertex_data> vertices;
  346. flow builder;
  347. };
  348. } // namespace entt
  349. #endif