From 956453facc7ee58f162b8e639bfb15e770fc4054 Mon Sep 17 00:00:00 2001 From: Kelwan Date: Thu, 23 May 2024 13:55:09 -0700 Subject: [PATCH] feat: Parallel system execution --- ecsact/entt/execution.hh | 20 +- ecsact/entt/wrapper/core.hh | 9 + rt_entt_codegen/core/BUILD.bazel | 5 +- rt_entt_codegen/core/core.hh | 5 + rt_entt_codegen/core/execute_systems.cc | 113 +++++++++--- rt_entt_codegen/core/init_registry_storage.cc | 19 +- rt_entt_codegen/core/print_sys_exec.cc | 174 ++++++++++++++---- rt_entt_codegen/rt_entt_codegen.cc | 9 + rt_entt_codegen/shared/BUILD.bazel | 13 +- rt_entt_codegen/shared/parallel.cc | 161 ++++++++++++++++ rt_entt_codegen/shared/parallel.hh | 11 ++ test/parallel/BUILD.bazel | 42 +++++ test/parallel/parallel_test.cc | 118 ++++++++++++ test/parallel/parallel_test.ecsact | 73 ++++++++ 14 files changed, 701 insertions(+), 71 deletions(-) create mode 100644 rt_entt_codegen/shared/parallel.cc create mode 100644 rt_entt_codegen/shared/parallel.hh create mode 100644 test/parallel/BUILD.bazel create mode 100644 test/parallel/parallel_test.cc create mode 100644 test/parallel/parallel_test.ecsact diff --git a/ecsact/entt/execution.hh b/ecsact/entt/execution.hh index b222bf8a..090ebc0b 100644 --- a/ecsact/entt/execution.hh +++ b/ecsact/entt/execution.hh @@ -68,7 +68,7 @@ struct actions_map { } template - auto as_action_span() -> std::span { + auto as_action_span() const -> const std::span { ecsact_action_id action_id = Action::id; if(!raw_value.contains(action_id)) { @@ -78,8 +78,8 @@ struct actions_map { auto& action_data_list = raw_value.at(action_id); auto action_list_data_ptr = action_data_list.data(); - return std::span{ - reinterpret_cast(action_list_data_ptr), + return std::span{ + reinterpret_cast(action_list_data_ptr), action_data_list.size(), }; } @@ -94,7 +94,8 @@ struct actions_map { template auto execute_system( // ::entt::registry& registry, - ecsact_system_execution_context* parent + ecsact_system_execution_context* parent, + const ecsact::entt::actions_map& actions_map ) -> void { static_assert(detail::unimplemented_by_codegen, R"( ----------------------------------------------------------------------------- @@ -114,8 +115,9 @@ auto execute_system( // */ template auto execute_actions( // - ::entt::registry& registry, - std::span action_range + ::entt::registry& registry, + ecsact_system_execution_context* parent, + const ecsact::entt::actions_map& actions_map ) -> void { static_assert(detail::unimplemented_by_codegen, R"( ----------------------------------------------------------------------------- @@ -126,6 +128,12 @@ auto execute_actions( // )"); } +using execute_fn_t = void (*)( + ::entt::registry& registry, + ecsact_system_execution_context* parent, + const ecsact::entt::actions_map& actions_map +); + /** * Allocates EnTT groups and storage if necessary for the system or action. * NOTE: Template specializations are made available via the codegen plugin diff --git a/ecsact/entt/wrapper/core.hh b/ecsact/entt/wrapper/core.hh index bdcf1bbf..4aa57c3e 100644 --- a/ecsact/entt/wrapper/core.hh +++ b/ecsact/entt/wrapper/core.hh @@ -411,6 +411,15 @@ inline auto prepare_component(ecsact_registry_id registry_id) -> void { } } +template +inline auto prepare_system(ecsact_registry_id registry_id) -> void { + using namespace ecsact::entt::detail; + auto& reg = ecsact::entt::get_registry(registry_id); + + reg.template storage>(); + reg.template storage>(); +} + template auto has_component_changed(entt::entity_id entity, V& view) -> bool { using detail::exec_itr_beforechange_storage; diff --git a/rt_entt_codegen/core/BUILD.bazel b/rt_entt_codegen/core/BUILD.bazel index 20cc7cd3..7b0eda33 100644 --- a/rt_entt_codegen/core/BUILD.bazel +++ b/rt_entt_codegen/core/BUILD.bazel @@ -11,7 +11,9 @@ cc_library( # keep sorted _CORE_CODEGEN_METHODS = { - "execute_systems": [], + "execute_systems": [ + "//rt_entt_codegen/shared:parallel", + ], "create_registry": [], "entity_matches": [ "//rt_entt_codegen/shared:sorting", @@ -22,6 +24,7 @@ _CORE_CODEGEN_METHODS = { "//rt_entt_codegen/shared:comps_with_caps", "//rt_entt_codegen/shared:sorting", "//rt_entt_codegen/shared:system_util", + "//rt_entt_codegen/shared:parallel", "@entt//:entt", "@ecsact_rt_entt//:lib", ], diff --git a/rt_entt_codegen/core/core.hh b/rt_entt_codegen/core/core.hh index 196cefc5..532d98df 100644 --- a/rt_entt_codegen/core/core.hh +++ b/rt_entt_codegen/core/core.hh @@ -6,6 +6,11 @@ namespace ecsact::rt_entt_codegen::core { +auto print_parallel_system_execute( + codegen_plugin_context& ctx, + const ecsact_entt_details& details +) -> void; + auto print_execute_systems( // codegen_plugin_context& ctx, const ecsact_entt_details& details diff --git a/rt_entt_codegen/core/execute_systems.cc b/rt_entt_codegen/core/execute_systems.cc index 8bb2651b..6193ad5a 100644 --- a/rt_entt_codegen/core/execute_systems.cc +++ b/rt_entt_codegen/core/execute_systems.cc @@ -1,14 +1,44 @@ #include "core.hh" +#include "rt_entt_codegen/shared/parallel.hh" #include "ecsact/lang-support/lang-cc.hh" #include "rt_entt_codegen/shared/util.hh" #include "ecsact/cpp_codegen_plugin_util.hh" constexpr auto METHOD_BODY_TOP = R"( -auto& reg = ecsact::entt::get_registry(registry_id); -auto actions = ecsact::entt::actions_map{}; +auto& registry = ecsact::entt::get_registry(registry_id); +auto actions_map = ecsact::entt::actions_map{}; )"; +auto ecsact::rt_entt_codegen::core::print_parallel_system_execute( + codegen_plugin_context& ctx, + const ecsact_entt_details& details +) -> void { + using ecsact::cc_lang_support::cpp_identifier; + using ecsact::cpp_codegen_plugin_util::block; + using ecsact::rt_entt_codegen::util::method_printer; + + ctx.write("template\n"); + auto printer = // + method_printer{ctx, "execute_parallel_cluster"} // + .parameter("::entt::registry&", "registry") + .parameter("ecsact_system_execution_context*", "parent_context") + .parameter("std::array", "system_arr") + .return_type("void"); + + block( + ctx, + "std::for_each(std::execution::par_unseq, system_arr.begin(), " + "system_arr.end(), [®istry](exec_entry_t pair)", + [&]() { + ctx.write("auto fn_ptr = pair.first;\n"); + ctx.write("auto& actions_map = pair.second;\n"); + ctx.write("fn_ptr(registry, nullptr, actions_map);"); + } + ); + ctx.write(");\n"); +} + auto ecsact::rt_entt_codegen::core::print_execute_systems( // codegen_plugin_context& ctx, const ecsact_entt_details& details @@ -33,7 +63,9 @@ auto ecsact::rt_entt_codegen::core::print_execute_systems( // ctx.indentation += 1; ctx.write("\n"); - ctx.write("actions.collect(i, execution_count, execution_options_list);\n"); + ctx.write( // + "actions_map.collect(i, execution_count, execution_options_list);\n" + ); block(ctx, "if(execution_options_list != nullptr)", [&] { ctx.write( @@ -45,26 +77,65 @@ auto ecsact::rt_entt_codegen::core::print_execute_systems( // }); }); - for(auto sys_like : details.top_execution_order) { - auto cpp_decl_name = cpp_identifier(ecsact::meta::decl_full_name(sys_like)); + auto parallel_system_cluster = + ecsact::rt_entt_codegen::parallel::get_parallel_execution_cluster( + ctx, + details, + details.top_execution_order + ); - if(details.is_action(sys_like)) { - ctx.write( - "ecsact::entt::execute_actions<", - cpp_decl_name, - ">(reg, actions.as_action_span<", - cpp_decl_name, - ">());\n" - ); - } else if(details.is_system(sys_like)) { - ctx.write( - "ecsact::entt::execute_system<", - cpp_decl_name, - ">(reg, nullptr);\n" - ); - } else { - ctx.write("// ??? unhandled ??? ", cpp_decl_name, "\n"); + for(const auto& systems_to_parallel : parallel_system_cluster) { + if(systems_to_parallel.size() == 1) { + auto sync_sys_id = systems_to_parallel[0]; + + auto sync_sys_name = + cpp_identifier(ecsact::meta::decl_full_name(sync_sys_id)); + + if(details.is_action(sync_sys_id)) { + ctx.write(std::format( + "ecsact::entt::execute_actions<{}>(registry, {}, " + "actions_map);\n", + sync_sys_name, + "nullptr" + )); + } + if(details.is_system(sync_sys_id)) { + ctx.write(std::format( + "ecsact::entt::execute_system<{}>(registry, {}, " + "actions_map);\n", + sync_sys_name, + "nullptr" + )); + } + continue; + } + + ctx.write("execute_parallel_cluster(registry, nullptr, "); + ctx.write(std::format( + "std::array {{\n", + systems_to_parallel.size() + )); + for(const auto system_like_id : systems_to_parallel) { + auto cpp_decl_name = + cpp_identifier(ecsact::meta::decl_full_name(system_like_id)); + + if(details.is_action(system_like_id)) { + ctx.write( + "exec_entry_t{&ecsact::entt::execute_actions<", + cpp_decl_name, + ">, actions_map},\n" + ); + } else if(details.is_system(system_like_id)) { + ctx.write( + "exec_entry_t{&ecsact::entt::execute_system<", + cpp_decl_name, + ">, actions_map},\n" + ); + } else { + ctx.write("// ??? unhandled ??? ", cpp_decl_name, "\n"); + } } + ctx.write("});\n"); } ctx.write("\nupdate_all_beforechange_storage(registry_id);\n"); diff --git a/rt_entt_codegen/core/init_registry_storage.cc b/rt_entt_codegen/core/init_registry_storage.cc index 816140c7..6cc7365b 100644 --- a/rt_entt_codegen/core/init_registry_storage.cc +++ b/rt_entt_codegen/core/init_registry_storage.cc @@ -19,11 +19,18 @@ auto ecsact::rt_entt_codegen::core::print_init_registry_storage( for(auto comp_id : details.all_components) { auto cpp_comp_name = cpp_identifier(decl_full_name(comp_id)); - ctx.write( - "ecsact::entt::wrapper::core::prepare_component<", - cpp_comp_name, - ">(registry_id)" - ); - ctx.write(";\n"); + ctx.write(std::format( + "ecsact::entt::wrapper::core::prepare_component<{}>(registry_id);\n", + cpp_comp_name + )); + } + + for(auto system_id : details.all_systems) { + auto cpp_sys_name = cpp_identifier(decl_full_name(system_id)); + + ctx.write(std::format( + "ecsact::entt::wrapper::core::prepare_system<{}>(registry_id);\n", + cpp_sys_name + )); } } diff --git a/rt_entt_codegen/core/print_sys_exec.cc b/rt_entt_codegen/core/print_sys_exec.cc index 4f1be00c..294616b5 100644 --- a/rt_entt_codegen/core/print_sys_exec.cc +++ b/rt_entt_codegen/core/print_sys_exec.cc @@ -16,6 +16,7 @@ #include "rt_entt_codegen/shared/comps_with_caps.hh" #include "rt_entt_codegen/shared/sorting.hh" #include "rt_entt_codegen/shared/system_util.hh" +#include "rt_entt_codegen/shared/parallel.hh" using capability_t = std::unordered_map; @@ -593,7 +594,8 @@ template std::remove_cvref_t>) static auto print_ecsact_entt_system_details( ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::ecsact_entt_system_details& details, + const ecsact::rt_entt_codegen::ecsact_entt_details& details, + const ecsact::rt_entt_codegen::ecsact_entt_system_details& sys_details, const print_ecsact_entt_system_details_options options ) -> void { using ecsact::cc_lang_support::cpp_identifier; @@ -660,7 +662,7 @@ static auto print_ecsact_entt_system_details( ); print_system_notify_views( ctx, - details, + sys_details, options.sys_like_id, options.registry_var_name ); @@ -670,7 +672,7 @@ static auto print_ecsact_entt_system_details( ctx, "view", options.registry_var_name, - details, + sys_details, additional_view_components ); @@ -696,15 +698,15 @@ static auto print_ecsact_entt_system_details( } ctx.write("\n"); - print_sys_exec_ctx_action(ctx, details, options.sys_like_id); - print_sys_exec_ctx_add(ctx, details, sys_caps); - print_sys_exec_ctx_remove(ctx, details, sys_caps, "view"); - print_sys_exec_ctx_get(ctx, details, "view"); - print_sys_exec_ctx_update(ctx, details, "view"); - print_sys_exec_ctx_has(ctx, details); - print_sys_exec_ctx_generate(ctx, details); + print_sys_exec_ctx_action(ctx, sys_details, options.sys_like_id); + print_sys_exec_ctx_add(ctx, sys_details, sys_caps); + print_sys_exec_ctx_remove(ctx, sys_details, sys_caps, "view"); + print_sys_exec_ctx_get(ctx, sys_details, "view"); + print_sys_exec_ctx_update(ctx, sys_details, "view"); + print_sys_exec_ctx_has(ctx, sys_details); + print_sys_exec_ctx_generate(ctx, sys_details); print_sys_exec_ctx_parent(ctx); - print_sys_exec_ctx_other(ctx, details); + print_sys_exec_ctx_other(ctx, sys_details); }); ctx.write("context;\n\n"); @@ -721,7 +723,7 @@ static auto print_ecsact_entt_system_details( ctx.write("context.parent_ctx = ", options.parent_context_var_name, ";\n"); ctx.write("context.view = &view;\n\n"); - auto other_view_names = print_other_contexts(ctx, details, options); + auto other_view_names = print_other_contexts(ctx, sys_details, options); block(ctx, "for(ecsact::entt::entity_id entity : view)", [&] { if(lazy_iteration_rate > 0) { @@ -758,19 +760,103 @@ static auto print_ecsact_entt_system_details( ctx.write("context.entity = entity;\n"); - for(auto child_sys_id : get_child_system_ids(options.sys_like_id)) { - auto child_details = ecsact_entt_system_details::from_system_like( - ecsact_id_cast(child_sys_id) - ); + auto child_system_ids = get_child_system_ids(options.sys_like_id); - auto child_system_name = cpp_identifier(decl_full_name(child_sys_id)); + std::vector child_system_like_ids{}; - ctx.write( - "ecsact::entt::execute_system<::" + child_system_name + ">(", - options.registry_var_name, - ", &context);\n" - ); + child_system_like_ids.resize(child_system_ids.size()); + + std::transform( + child_system_ids.begin(), + child_system_ids.end(), + child_system_like_ids.begin(), + [](auto system_id) { + return ecsact_id_cast(system_id); + } + ); + + if(!child_system_like_ids.empty()) { + if(child_system_ids.size() == 1) { + // TODO(Kelwan): Make use case system agnostic when we support + // nested Action systems + // Issue: https://github.com/ecsact-dev/ecsact_parse/issues/154 + for(auto child_sys_id : get_child_system_ids(options.sys_like_id)) { + auto child_details = ecsact_entt_system_details::from_system_like( + ecsact_id_cast(child_sys_id) + ); + + auto child_system_name = cpp_identifier(decl_full_name(child_sys_id)); + + ctx.write( + "ecsact::entt::execute_system<::" + child_system_name + ">(", + options.registry_var_name, + ", &context, {});\n" + ); + } + } else { + auto parallel_system_cluster = + ecsact::rt_entt_codegen::parallel::get_parallel_execution_cluster( + ctx, + details, + child_system_like_ids + ); + + for(const auto& systems_to_parallel : parallel_system_cluster) { + if(systems_to_parallel.size() == 1) { + auto sync_sys_id = systems_to_parallel[0]; + + auto sync_sys_name = + cpp_identifier(ecsact::meta::decl_full_name(sync_sys_id)); + + if(details.is_action(sync_sys_id)) { + ctx.write(std::format( + "ecsact::entt::execute_actions<{}>(registry, {}, " + "actions_map);\n", + sync_sys_name, + "parent_context" + )); + } + if(details.is_system(sync_sys_id)) { + ctx.write(std::format( + "ecsact::entt::execute_system<{}>(registry, {}, " + "actions_map);\n", + sync_sys_name, + "parent_context" + )); + } + continue; + } + + ctx.write("execute_parallel_cluster(registry, parent_context, "); + ctx.write(std::format( + "std::array {{\n", + systems_to_parallel.size() + )); + for(const auto system_like_id : systems_to_parallel) { + auto cpp_decl_name = + cpp_identifier(ecsact::meta::decl_full_name(system_like_id)); + + if(details.is_action(system_like_id)) { + ctx.write( + "exec_entry_t{&ecsact::entt::execute_actions<", + cpp_decl_name, + ">, actions_map},\n" + ); + } else if(details.is_system(system_like_id)) { + ctx.write( + "exec_entry_t{&ecsact::entt::execute_system<", + cpp_decl_name, + ">, actions_map},\n" + ); + } else { + ctx.write("// ??? unhandled ??? ", cpp_decl_name, "\n"); + } + } + ctx.write("});\n"); + } + } } + ctx.write("\n"); if(!other_view_names.empty()) { @@ -861,7 +947,7 @@ static auto print_ecsact_entt_system_details( ctx, "view_no_pending_lazy_", options.registry_var_name, - details + sys_details ); ctx.write("auto view_no_pending_lazy_count_ = 0;\n"); @@ -903,7 +989,7 @@ static auto print_ecsact_entt_system_details( print_apply_pendings( ctx, - details, + sys_details, options.sys_like_id, options.registry_var_name ); @@ -1018,6 +1104,7 @@ static auto print_trivial_system_like( method_printer{ctx, "ecsact::entt::execute_system<::" + system_name + ">"} .parameter("::entt::registry&", "registry") .parameter("ecsact_system_execution_context*", "parent_context") + .parameter("const ecsact::entt::actions_map&", "actions_map") .return_type("void"); ecsact::rt_entt_codegen::util::make_view(ctx, "view", "registry", details); @@ -1050,7 +1137,8 @@ static auto print_trivial_system_like( static auto print_execute_system_template_specialization( ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::ecsact_entt_system_details& details, + const ecsact::rt_entt_codegen::ecsact_entt_details& details, + const ecsact::rt_entt_codegen::ecsact_entt_system_details& sys_details, ecsact_system_id system_id ) -> void { using ecsact::cc_lang_support::cpp_identifier; @@ -1063,7 +1151,7 @@ static auto print_execute_system_template_specialization( if(is_trivial_system(ecsact_id_cast(system_id))) { print_trivial_system_like( ctx, - details, + sys_details, ecsact_id_cast(system_id) ); return; @@ -1075,6 +1163,7 @@ static auto print_execute_system_template_specialization( method_printer{ctx, "ecsact::entt::execute_system<::" + system_name + ">"} .parameter("::entt::registry&", "registry") .parameter("ecsact_system_execution_context*", "parent_context") + .parameter("const ecsact::entt::actions_map&", "actions_map") .return_type("void"); ctx.write( @@ -1088,6 +1177,7 @@ static auto print_execute_system_template_specialization( print_ecsact_entt_system_details( ctx, details, + sys_details, { .sys_like_id = system_id, .system_name = system_name, @@ -1100,7 +1190,8 @@ static auto print_execute_system_template_specialization( static auto print_execute_actions_template_specialization( ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::ecsact_entt_system_details& details, + const ecsact::rt_entt_codegen::ecsact_entt_details& details, + const ecsact::rt_entt_codegen::ecsact_entt_system_details& sys_details, ecsact_action_id action_id ) -> void { using ecsact::cc_lang_support::cpp_identifier; @@ -1112,7 +1203,7 @@ static auto print_execute_actions_template_specialization( if(is_trivial_system(ecsact_id_cast(action_id))) { print_trivial_system_like( ctx, - details, + sys_details, ecsact_id_cast(action_id) ); return; @@ -1127,7 +1218,8 @@ static auto print_execute_actions_template_specialization( auto printer = // method_printer{ctx, method_name} .parameter("::entt::registry&", "registry") - .parameter("std::span<::" + cpp_system_name + " const*>", "actions") + .parameter("ecsact_system_execution_context", "*parent_context") + .parameter("const ecsact::entt::actions_map&", "actions_map") .return_type("void"); ctx.write( @@ -1138,10 +1230,17 @@ static auto print_execute_actions_template_specialization( block(ctx, "if(system_impl == nullptr)", [&] { ctx.write("return;"); }); + ctx.write( + "auto actions = actions_map.as_action_span<", + cpp_system_name, + ">();\n" + ); + block(ctx, "for(auto action : actions)", [&] { print_ecsact_entt_system_details( ctx, details, + sys_details, { .sys_like_id = action_id, .system_name = cpp_system_name, @@ -1155,26 +1254,27 @@ static auto print_execute_actions_template_specialization( template static auto print_child_execution_system_like_template_specializations( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::ecsact_entt_system_details& details, - SystemLikeID sys_like_id + ecsact::codegen_plugin_context& ctx, + const ecsact::rt_entt_codegen::ecsact_entt_details& details, + SystemLikeID sys_like_id ) -> void { using ecsact::rt_entt_codegen::ecsact_entt_system_details; for(auto child_sys_id : ecsact::meta::get_child_system_ids(sys_like_id)) { - auto sys_details = ecsact_entt_system_details::from_system_like( + auto child_sys_details = ecsact_entt_system_details::from_system_like( static_cast(child_sys_id) ); print_execute_system_template_specialization( ctx, - sys_details, + details, + child_sys_details, static_cast(child_sys_id) ); print_child_execution_system_like_template_specializations( ctx, - sys_details, + details, static_cast(child_sys_id) ); } @@ -1195,19 +1295,21 @@ auto ecsact::rt_entt_codegen::core:: print_child_execution_system_like_template_specializations( ctx, - sys_details, + details, sys_like_id ); if(details.is_system(sys_like_id)) { print_execute_system_template_specialization( ctx, + details, sys_details, static_cast(sys_like_id) ); } else if(details.is_action(sys_like_id)) { print_execute_actions_template_specialization( ctx, + details, sys_details, static_cast(sys_like_id) ); diff --git a/rt_entt_codegen/rt_entt_codegen.cc b/rt_entt_codegen/rt_entt_codegen.cc index 87036032..5b2d7b5f 100644 --- a/rt_entt_codegen/rt_entt_codegen.cc +++ b/rt_entt_codegen/rt_entt_codegen.cc @@ -54,6 +54,8 @@ void ecsact_codegen_plugin( inc_header(ctx, "ecsact/entt/wrapper/core.hh"); inc_header(ctx, "ecsact/entt/wrapper/dynamic.hh"); inc_header(ctx, "ecsact/entt/error_check.hh"); + ctx.write("#include \n"); + ctx.write("\n"); inc_package_header(ctx, package_id); for(auto dep : ecsact::meta::get_dependencies(package_id)) { @@ -62,6 +64,12 @@ void ecsact_codegen_plugin( } ctx.write("\n"); + ctx.write("// test1234\n"); + ctx.write( + "using exec_entry_t = std::pair;\n\n" + ); + init_global(ctx, "registries"); init_global(ctx, "last_registry_id"); init_global(ctx, "system_impls"); @@ -209,6 +217,7 @@ void ecsact_codegen_plugin( core::print_system_marker_remove_fn(ctx, details); core::print_add_sys_beforestorage_fn(ctx, details); core::print_entity_sorting_components(ctx, details); + core::print_parallel_system_execute(ctx, details); core::print_check_error_template_specializations(ctx, details); core::print_execute_system_like_template_specializations(ctx, details); core::print_init_registry_storage(ctx, details); diff --git a/rt_entt_codegen/shared/BUILD.bazel b/rt_entt_codegen/shared/BUILD.bazel index cb835799..68501bd7 100644 --- a/rt_entt_codegen/shared/BUILD.bazel +++ b/rt_entt_codegen/shared/BUILD.bazel @@ -19,8 +19,8 @@ cc_library( cc_library( name = "sorting", - hdrs = ["sorting.hh"], srcs = ["sorting.cc"], + hdrs = ["sorting.hh"], copts = copts, deps = [ ":ecsact_entt_details", @@ -68,3 +68,14 @@ cc_library( "@ecsact_runtime//:meta", ], ) + +cc_library( + name = "parallel", + srcs = ["parallel.cc"], + hdrs = ["parallel.hh"], + copts = copts, + deps = [ + ":ecsact_entt_details", + "@ecsact_lang_cpp//:cpp_codegen_plugin_util", + ], +) diff --git a/rt_entt_codegen/shared/parallel.cc b/rt_entt_codegen/shared/parallel.cc new file mode 100644 index 00000000..14ae1c9c --- /dev/null +++ b/rt_entt_codegen/shared/parallel.cc @@ -0,0 +1,161 @@ +#include "parallel.hh" + +#include +#include + +#include "ecsact/lang-support/lang-cc.hh" +#include "ecsact/cpp_codegen_plugin_util.hh" + +static auto loop_iterator( + std::vector& system_list, + const std::vector::iterator begin, + std::vector>& parallel_system_cluster +) -> void; + +auto ecsact::rt_entt_codegen::parallel::get_parallel_execution_cluster( + ecsact::codegen_plugin_context& ctx, + const ecsact::rt_entt_codegen::ecsact_entt_details& details, + std::vector system_list, + std::string parent_context +) -> std::vector> { + using ecsact::cc_lang_support::cpp_identifier; + using ecsact::cpp_codegen_plugin_util::block; + + auto parallel_system_cluster = + std::vector>{}; + + loop_iterator(system_list, system_list.begin(), parallel_system_cluster); + + return parallel_system_cluster; +} + +static auto is_capability_safe(ecsact_system_capability capability) -> bool { + if(capability == ECSACT_SYS_CAP_ADDS || + capability == ECSACT_SYS_CAP_REMOVES || + capability == ECSACT_SYS_CAP_READWRITE || + capability == ECSACT_SYS_CAP_WRITEONLY || + capability == ECSACT_SYS_CAP_OPTIONAL_WRITEONLY || + capability == ECSACT_SYS_CAP_OPTIONAL_READWRITE) { + return false; + } + return true; +} + +static auto loop_iterator( + std::vector& system_list, + const std::vector::iterator begin, + std::vector>& parallel_system_cluster +) -> void { + std::vector parallel_system_list; + auto unsafe_comps = std::set{}; + + using ecsact::meta::decl_full_name; + + for(auto iterator = begin; iterator != system_list.end(); iterator++) { + auto sys_like_id = *iterator; + auto capabilities = ecsact::meta::system_capabilities(sys_like_id); + + auto generate_ids = ecsact::meta::get_system_generates_ids(sys_like_id); + + if(!generate_ids.empty()) { + if(!parallel_system_list.empty()) { + parallel_system_cluster.push_back(parallel_system_list); + } + + parallel_system_cluster.push_back( + std::vector{sys_like_id} + ); + loop_iterator( + system_list, + std::next(iterator, 1), + parallel_system_cluster + ); + return; + } + + std::set child_unsafe_comps{}; + + auto child_systems = ecsact::meta::get_child_system_ids(sys_like_id); + + for(auto child_sys_id : child_systems) { + auto cpp_system_name = decl_full_name(child_sys_id); + auto child_capabilities = ecsact::meta::system_capabilities(child_sys_id); + for(auto const [child_comp_id, child_capability] : child_capabilities) { + if(unsafe_comps.contains(child_comp_id)) { + if(child_capability == ECSACT_SYS_CAP_READONLY || + child_capability == ECSACT_SYS_CAP_OPTIONAL_READONLY) { + parallel_system_cluster.push_back(parallel_system_list); + loop_iterator(system_list, iterator, parallel_system_cluster); + return; + } + } + + if(!is_capability_safe(child_capability)) { + if(unsafe_comps.contains(child_comp_id)) { + parallel_system_cluster.push_back(parallel_system_list); + loop_iterator(system_list, iterator, parallel_system_cluster); + return; + } else { + child_unsafe_comps.insert(child_comp_id); + } + } + } + } + + for(const auto [comp_id, capability] : capabilities) { + auto cpp_name = decl_full_name(comp_id); + + if(unsafe_comps.contains(comp_id)) { + if(capability == ECSACT_SYS_CAP_READONLY || + capability == ECSACT_SYS_CAP_OPTIONAL_READONLY) { + parallel_system_cluster.push_back(parallel_system_list); + loop_iterator(system_list, iterator, parallel_system_cluster); + return; + } + } + + if(!is_capability_safe(capability)) { + if(!unsafe_comps.contains(comp_id)) { + unsafe_comps.insert(comp_id); + } else { + parallel_system_cluster.push_back(parallel_system_list); + loop_iterator(system_list, iterator, parallel_system_cluster); + return; + } + } + + auto other_fields = + ecsact::meta::system_association_fields(sys_like_id, comp_id); + + for(auto field_id : other_fields) { + auto other_capabilities = ecsact::meta::system_association_capabilities( + sys_like_id, + comp_id, + field_id + ); + + for(const auto [other_comp_id, other_capability] : other_capabilities) { + auto cpp_name = decl_full_name(other_comp_id); + if(!is_capability_safe(other_capability)) { + if(!unsafe_comps.contains(other_comp_id)) { + unsafe_comps.insert(other_comp_id); + } else { + parallel_system_cluster.push_back(parallel_system_list); + loop_iterator(system_list, iterator, parallel_system_cluster); + return; + } + } + } + } + } + + for(auto unsafe_comp : child_unsafe_comps) { + if(!unsafe_comps.contains(unsafe_comp)) { + unsafe_comps.insert(unsafe_comp); + } + } + + parallel_system_list.push_back(sys_like_id); + } + parallel_system_cluster.push_back(parallel_system_list); +} diff --git a/rt_entt_codegen/shared/parallel.hh b/rt_entt_codegen/shared/parallel.hh new file mode 100644 index 00000000..dcf91f9f --- /dev/null +++ b/rt_entt_codegen/shared/parallel.hh @@ -0,0 +1,11 @@ +#include "ecsact/codegen/plugin.hh" +#include "rt_entt_codegen/shared/ecsact_entt_details.hh" + +namespace ecsact::rt_entt_codegen::parallel { +auto get_parallel_execution_cluster( + ecsact::codegen_plugin_context& ctx, + const ecsact::rt_entt_codegen::ecsact_entt_details& details, + std::vector system_list, + std::string parent_context = "nullptr" +) -> std::vector>; +} diff --git a/test/parallel/BUILD.bazel b/test/parallel/BUILD.bazel new file mode 100644 index 00000000..06b93480 --- /dev/null +++ b/test/parallel/BUILD.bazel @@ -0,0 +1,42 @@ +load("@ecsact_rt_entt//bazel:copts.bzl", "copts") +load("@ecsact_rt_entt//runtime:index.bzl", "ecsact_entt_runtime") +load("@rules_cc//cc:defs.bzl", "cc_test") +load("@rules_ecsact//ecsact:defs.bzl", "ecsact_codegen") + +ecsact_codegen( + name = "ecsact_cc_system_impl_srcs", + srcs = [ + "parallel_test.ecsact", + ], + output_directory = "_ecsact_cc_system_impl_srcs", + plugins = [ + "@ecsact_lang_cpp//cpp_systems_source_codegen", + ], +) + +ecsact_entt_runtime( + name = "runtime", + srcs = [ + "parallel_test.ecsact", + ], + ECSACT_ENTT_RUNTIME_PACKAGE = "::parallel_test::package", + ECSACT_ENTT_RUNTIME_USER_HEADER = "parallel_test.ecsact.meta.hh", + system_impls = [ + "dynamic", + ], +) + +cc_test( + name = "test", + srcs = [ + "parallel_test.cc", + ":ecsact_cc_system_impl_srcs", + ], + args = ["--gtest_catch_exceptions=0"], + copts = copts, + deps = [ + ":runtime", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) diff --git a/test/parallel/parallel_test.cc b/test/parallel/parallel_test.cc new file mode 100644 index 00000000..0a6aaed6 --- /dev/null +++ b/test/parallel/parallel_test.cc @@ -0,0 +1,118 @@ +#include "gtest/gtest.h" + +#include + +#include "ecsact/runtime/core.hh" +#include "ecsact/runtime/dynamic.h" + +#include "parallel_test.ecsact.hh" +#include "parallel_test.ecsact.systems.hh" + +void parallel_test::ActionParallelA::impl(context& ctx) { +} + +void parallel_test::EntityAssociationConflictA::impl(context& ctx) { +} + +void parallel_test::ParentSystemConflictA::impl(context& ctx) { +} + +void parallel_test::ParentSystemConflictA::NestedSystemConflictA::impl( + context& ctx +) { +} + +void parallel_test::ParentSystemConflictA::NestedSystemConflictB::impl( + context& ctx +) { +} + +void parallel_test::NestedSystemNoConflict::impl(context& ctx) { +} + +void parallel_test::NestedSystemNoConflict::NestedSystemNoConflictA::impl( + context& ctx +) { +} + +void parallel_test::NestedSystemNoConflict::NestedSystemNoConflictB::impl( + context& ctx +) { +} + +void parallel_test::ReadParallelA::impl(context& ctx) { +} + +void parallel_test::ReadParallelA::ReadParallelAChildSystem::impl(context& ctx +) { +} + +void parallel_test::ReadWriteParallelA::impl(context& ctx) { + auto comp = ctx.get(); + + comp.val += 1; + ctx.update(comp); +} + +void parallel_test::ReadWriteParallelB::impl(context& ctx) { + auto comp = ctx.get(); + + comp.val += 1; + ctx.update(comp); +} + +void parallel_test::ReadParallelB::impl(context& ctx) { +} + +void parallel_test::ReadParallelBB::impl(context& ctx) { +} + +void parallel_test::ParentWithSharedComponentA::impl(context& ctx) { +} + +void parallel_test::ParentWithSharedComponentA::ChildWithSharedComponentA::impl( + context& ctx +) { +} + +TEST(Parallel, RunEntitiesinParallel) { + auto reg = ecsact::core::registry("RunEntitiesInParallel"); + + auto entity_a = reg.create_entity(); + auto entity_b = reg.create_entity(); + + ecsact_set_system_execution_impl( + ecsact_id_cast(parallel_test::ReadWriteParallelA::id + ), + parallel_test__ReadWriteParallelA + ); + + ecsact_set_system_execution_impl( + ecsact_id_cast(parallel_test::ReadWriteParallelB::id + ), + parallel_test__ReadWriteParallelB + ); + + reg.add_component(entity_a, parallel_test::ParallelA{}); + reg.add_component(entity_b, parallel_test::ParallelB{}); + + reg.execute_systems(1000); + + auto comp_a = reg.get_component(entity_a); + auto comp_b = reg.get_component(entity_b); + + ASSERT_EQ(comp_a.val, 1000); + ASSERT_EQ(comp_b.val, 1000); + + ecsact_set_system_execution_impl( + ecsact_id_cast(parallel_test::ReadWriteParallelA::id + ), + nullptr + ); + + ecsact_set_system_execution_impl( + ecsact_id_cast(parallel_test::ReadWriteParallelB::id + ), + nullptr + ); +} diff --git a/test/parallel/parallel_test.ecsact b/test/parallel/parallel_test.ecsact new file mode 100644 index 00000000..36fa64b5 --- /dev/null +++ b/test/parallel/parallel_test.ecsact @@ -0,0 +1,73 @@ +main package parallel_test; + +component ParallelA { + i32 val; +} +component ParallelB { + i32 val; +} +component ParallelC { + entity target; +} + +action ActionParallelA { + readonly ParallelA; +} + +system EntityAssociationConflictA { + readonly ParallelC with target { + readwrite ParallelA; + } +} + +system ParentSystemConflictA { + readonly ParallelA; + system NestedSystemConflictA { + readwrite ParallelA; + } + system NestedSystemConflictB { + readwrite ParallelA; + } +} + +system NestedSystemNoConflict { + readonly ParallelA; + system NestedSystemNoConflictA { + readonly ParallelB; + } + system NestedSystemNoConflictB { + readonly ParallelB; + } +} + +system ReadParallelA { + readonly ParallelB; + system ReadParallelAChildSystem { + readwrite ParallelA; + } +} + +system ReadWriteParallelA { + readwrite ParallelA; +} + +system ReadWriteParallelB { + readwrite ParallelB; +} + +system ReadParallelB { + readonly ParallelB; +} + +system ReadParallelBB { + readonly ParallelB; +} + +system ParentWithSharedComponentA { + readwrite ParallelA; + system ChildWithSharedComponentA { + readwrite ParallelA; + } +} + +