diff --git a/BUILD.bazel b/BUILD.bazel index 6c3a6b4..f59fa55 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -19,6 +19,10 @@ cc_test( name = "test", visibility = ["//visibility:private"], copts = copts, + defines = [ + "ECSACT_CORE_API=", + "ECSACT_DYNAMIC_API=", + ], srcs = [ "build_test.cc", "//runtime:sources", diff --git a/MODULE.bazel b/MODULE.bazel index 19ac8de..ad44bda 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -6,13 +6,13 @@ module( bazel_dep(name = "rules_cc", version = "0.0.9") bazel_dep(name = "bazel_skylib", version = "1.6.1") -bazel_dep(name = "rules_ecsact", version = "0.5.7") -bazel_dep(name = "ecsact_runtime", version = "0.6.9") -bazel_dep(name = "ecsact_lang_cpp", version = "0.4.7") +bazel_dep(name = "rules_ecsact", version = "0.5.8") +bazel_dep(name = "ecsact_runtime", version = "0.7.0") +bazel_dep(name = "ecsact_lang_cpp", version = "0.4.10") bazel_dep(name = "boost.mp11", version = "1.83.0.bzl.1") bazel_dep(name = "entt", version = "3.12.2") -bazel_dep(name = "ecsact_codegen", version = "0.4.1") -bazel_dep(name = "ecsact_cli", version = "0.3.16") +bazel_dep(name = "ecsact_codegen", version = "0.4.3") +bazel_dep(name = "ecsact_cli", version = "0.3.19") bazel_dep(name = "xxhash", version = "0.8.2") bazel_dep(name = "googletest", version = "1.14.0.bcr.1") bazel_dep(name = "boost.dll", version = "1.83.0.bzl.2") diff --git a/build_test.cc b/build_test.cc index a93afe1..aeb3e9f 100644 --- a/build_test.cc +++ b/build_test.cc @@ -1,4 +1,4 @@ -// Include all headers fot the sake of a quick build test +// Include all headers for the sake of a quick build test #include "entt/entt.hpp" // IWYU pragma: keep #include "ecsact/entt/detail/apply_pending.hh" // IWYU pragma: keep #include "ecsact/entt/detail/bytes.hh" // IWYU pragma: keep @@ -8,6 +8,7 @@ #include "ecsact/entt/detail/internal_markers.hh" // IWYU pragma: keep #include "ecsact/entt/detail/registry.hh" // IWYU pragma: keep #include "ecsact/entt/detail/system_execution_context.hh" // IWYU pragma: keep +#include "ecsact/entt/detail/apply_component_stream_data.hh" // IWYU pragma: keep #include "ecsact/entt/entity.hh" // IWYU pragma: keep #include "ecsact/entt/error_check.hh" // IWYU pragma: keep #include "ecsact/entt/event_markers.hh" // IWYU pragma: keep @@ -15,6 +16,9 @@ #include "ecsact/entt/registry_util.hh" // IWYU pragma: keep #include "ecsact/entt/wrapper/core.hh" // IWYU pragma: keep #include "ecsact/entt/wrapper/dynamic.hh" // IWYU pragma: keep +#include "ecsact/entt/stream_registries.hh" // IWYU pragma: keep +#include "ecsact/runtime/common.h" // IWYU pragma: keep +#include "entt/entity/registry.hpp" // IWYU pragma: keep // default assign some global vars for the sake of compiling only #define MOCK_DEF_GLOBAL(Name) \ @@ -30,6 +34,7 @@ MOCK_DEF_GLOBAL(has_component_fns); MOCK_DEF_GLOBAL(update_component_fns); MOCK_DEF_GLOBAL(remove_component_fns); MOCK_DEF_GLOBAL(exec_ctx_action_fns); +MOCK_DEF_GLOBAL(ecsact_stream_fns); auto main() -> int { // This is only here to get compile commands working diff --git a/ecsact/entt/detail/apply_component_stream_data.hh b/ecsact/entt/detail/apply_component_stream_data.hh new file mode 100644 index 0000000..18c57bc --- /dev/null +++ b/ecsact/entt/detail/apply_component_stream_data.hh @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "ecsact/entt/detail/internal_markers.hh" + +namespace ecsact::entt::detail { +template +auto apply_component_stream_data( + ::entt::registry& main_reg, + ::entt::registry& stream_reg +) -> void { + auto view = main_reg.template view< + C>(::entt::exclude>); + + for(auto entity : view) { + if(!stream_reg.any_of(entity)) { + continue; + } + + auto& in_component = stream_reg.get(entity); + if(main_reg.any_of(entity)) { + auto& current_comp = main_reg.get(entity); + + auto& beforechange = + main_reg.template get>(entity); + + if(!beforechange.has_update_occurred) { + beforechange.value = current_comp; + beforechange.has_update_occurred = true; + } + current_comp = in_component; + } + } +} +} // namespace ecsact::entt::detail diff --git a/ecsact/entt/detail/globals.hh b/ecsact/entt/detail/globals.hh index defbb14..db4a9e0 100644 --- a/ecsact/entt/detail/globals.hh +++ b/ecsact/entt/detail/globals.hh @@ -8,6 +8,7 @@ #include "ecsact/runtime/dynamic.h" #include "ecsact/entt/detail/system_execution_context.hh" #include "ecsact/entt/detail/registry.hh" +#include "ecsact/entt/stream_registries.hh" /** * A small set of globals expected to be available the ecsact_rt_entt_codegen @@ -15,6 +16,12 @@ */ namespace ecsact::entt::detail::globals { +/** + * Holds and handles the individual registries for each thread that's passed in + * from ecsact_stream. + */ +extern stream::stream_registries stream_registries; + /** * Ecsact registry ID mapped to EnTT registry instance. */ @@ -91,6 +98,16 @@ extern const std::unordered_map< // decltype(&ecsact_has_component)> has_component_fns; +/** + * ecsact_stream fn pointers + * + * NOTE: This gets is filled in by ecsact_rt_entt_codegen + */ +extern const std::unordered_map< // + ecsact_component_id, + decltype(&ecsact_stream)> + ecsact_stream_fns; + /** * ecsact_system_execution_context_action fn pointers * diff --git a/ecsact/entt/detail/internal_markers.hh b/ecsact/entt/detail/internal_markers.hh index cb6d3e6..d72b783 100644 --- a/ecsact/entt/detail/internal_markers.hh +++ b/ecsact/entt/detail/internal_markers.hh @@ -51,6 +51,14 @@ struct pending_add { template struct pending_remove {}; +/** + * This flag is added to a system when its requirements contain a streaming + * component. If the ecsact_stream is toggled to systems, this component gets + * added and systems will run. + */ +template +struct run_on_stream {}; + struct created_entity { ecsact_placeholder_entity_id placeholder_entity_id; }; @@ -66,6 +74,9 @@ struct system_sorted { template struct pending_lazy_execution {}; +/** + * Flags a notify system to run upon fulfilling its requirements + */ template struct run_system {}; diff --git a/ecsact/entt/detail/system_execution_context.hh b/ecsact/entt/detail/system_execution_context.hh index 638683e..de34752 100644 --- a/ecsact/entt/detail/system_execution_context.hh +++ b/ecsact/entt/detail/system_execution_context.hh @@ -37,21 +37,25 @@ struct ecsact_system_execution_context { ) -> void = 0; virtual auto remove( // - ecsact_component_like_id component_id + ecsact_component_like_id component_id, + const void* indexed_fields ) -> void = 0; virtual auto get( // ecsact_component_like_id component_id, - void* out_component_data + void* out_component_data, + const void* indexed_fields ) -> void = 0; virtual auto update( // ecsact_component_like_id component_id, - const void* component_data + const void* component_data, + const void* indexed_fields ) -> void = 0; virtual auto has( // - ecsact_component_like_id component_id + ecsact_component_like_id component_id, + const void* indexed_fields ) -> bool = 0; virtual auto generate( // @@ -60,6 +64,12 @@ struct ecsact_system_execution_context { const void** components_data ) -> void = 0; + virtual auto stream_toggle( // + ecsact_component_id component_id, + bool enable_stream, + const void* indexed_fields + ) -> void = 0; + virtual auto parent() -> const ecsact_system_execution_context* = 0; virtual auto other( // diff --git a/ecsact/entt/registry_util.hh b/ecsact/entt/registry_util.hh index bd3d51d..8213c5b 100644 --- a/ecsact/entt/registry_util.hh +++ b/ecsact/entt/registry_util.hh @@ -28,6 +28,8 @@ inline auto create_registry() ); auto& registry = registries[registry_id]; + ecsact::entt::detail::globals::stream_registries.add_registry(registry_id); + return {registry_id, std::ref(registry)}; } diff --git a/ecsact/entt/stream_registries.hh b/ecsact/entt/stream_registries.hh new file mode 100644 index 0000000..a460c81 --- /dev/null +++ b/ecsact/entt/stream_registries.hh @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ecsact/runtime/common.h" +#include "ecsact/entt/entity.hh" + +namespace ecsact::entt::stream::detail { +using child_reg_thread_map = + std::unordered_map>; +using reg_thread_map = + std::unordered_map; +} // namespace ecsact::entt::stream::detail + +namespace ecsact::entt::stream { +class stream_registries { +private: + detail::reg_thread_map registry_stream_threads; + std::shared_mutex stream_mutex; + + template + auto ensure_and_add_entity_and_component( + std::unique_ptr<::entt::registry>& registry, + ::ecsact::entt::entity_id entity, + const C& component + ) -> void { + if(!registry->valid(entity)) { + auto new_entity = registry->create(entity); + assert(new_entity == entity.as_entt()); + } + + registry->template emplace_or_replace(entity, component); + } + +public: + stream_registries() = default; + + template + auto handle_stream( + ecsact_registry_id registry_id, + ecsact_entity_id entity_id, + const C& component + ) -> void { + // Add to map if new threads/registries are introduced + + auto thread_id = std::this_thread::get_id(); + auto entity = ::ecsact::entt::entity_id(entity_id); + + std::shared_lock shared_lk(stream_mutex); + std::unique_lock lk(stream_mutex, std::defer_lock); + + auto reg_threads_itr = registry_stream_threads.find(registry_id); + assert(reg_threads_itr != registry_stream_threads.end()); + + auto& reg_threads = reg_threads_itr->second; + auto reg_thread_itr = reg_threads.find(thread_id); + + if(reg_thread_itr == reg_threads.end()) { + auto registry = std::make_unique<::entt::registry>(); + ensure_and_add_entity_and_component(registry, entity, component); + shared_lk.unlock(); + lk.lock(); + reg_threads.insert( + reg_threads.end(), + std::pair(thread_id, std::move(registry)) + ); + lk.unlock(); + } else { + auto& registry = reg_thread_itr->second; + ensure_and_add_entity_and_component(registry, entity, component); + } + } + + auto get_stream_registries() + -> std::vector>; + auto add_registry(ecsact_registry_id) -> void; +}; +} // namespace ecsact::entt::stream diff --git a/ecsact/entt/wrapper/core.hh b/ecsact/entt/wrapper/core.hh index 9f3f7f0..9c53cf3 100644 --- a/ecsact/entt/wrapper/core.hh +++ b/ecsact/entt/wrapper/core.hh @@ -9,6 +9,7 @@ #include "ecsact/entt/registry_util.hh" #include "ecsact/entt/error_check.hh" #include "ecsact/entt/detail/execution_events_collector.hh" +#include "ecsact/entt/detail/globals.hh" namespace ecsact::entt::wrapper::core { @@ -17,8 +18,13 @@ inline auto has_component( // ecsact_registry_id registry_id, ecsact_entity_id entity_id, [[maybe_unused]] ecsact_component_id component_id, - ... + const void* indexed_fields ) -> bool { + static_assert( + !C::has_assoc_fields, + "Ecsact RT EnTT doesn't support indexed fields (yet)" + ); + auto& reg = ecsact::entt::get_registry(registry_id); auto entity = ecsact::entt::entity_id{entity_id}; assert(C::id == component_id); @@ -30,8 +36,13 @@ inline auto get_component( ecsact_registry_id registry_id, ecsact_entity_id entity_id, [[maybe_unused]] ecsact_component_id component_id, - ... + const void* indexed_fields ) -> const void* { + static_assert( + !C::has_assoc_fields, + "Ecsact RT EnTT doesn't support indexed fields (yet)" + ); + if constexpr(std::is_empty_v) { static C emptyVal; return &emptyVal; @@ -123,10 +134,15 @@ inline auto update_component( // ecsact_entity_id entity_id, [[maybe_unused]] ecsact_component_id component_id, const void* component_data, - ... + const void* indexed_fields ) -> ecsact_update_error { using ecsact::entt::detail::exec_beforechange_storage; + static_assert( + !C::has_assoc_fields, + "Ecsact RT EnTT doesn't support indexed fields (yet)" + ); + auto& reg = ecsact::entt::get_registry(registry_id); auto entity = ecsact::entt::entity_id{entity_id}; assert(C::id == component_id); @@ -156,10 +172,15 @@ inline auto update_component_exec_options( // ecsact_entity_id entity_id, [[maybe_unused]] ecsact_component_id component_id, const void* component_data, - ... + const void* indexed_fields ) -> ecsact_update_error { using ecsact::entt::detail::exec_beforechange_storage; + static_assert( + !C::has_assoc_fields, + "Ecsact RT EnTT doesn't support indexed fields (yet)" + ); + auto& reg = ecsact::entt::get_registry(registry_id); auto entity = ecsact::entt::entity_id{entity_id}; assert(C::id == component_id); @@ -193,8 +214,13 @@ auto remove_component( ecsact_registry_id registry_id, ecsact_entity_id entity_id, [[maybe_unused]] ecsact_component_id component_id, - ... + const void* indexed_fields ) -> void { + static_assert( + !C::has_assoc_fields, + "Ecsact RT EnTT doesn't support indexed fields (yet)" + ); + auto& reg = ecsact::entt::get_registry(registry_id); auto entity = ecsact::entt::entity_id{entity_id}; assert(C::id == component_id); @@ -213,10 +239,15 @@ auto remove_component_exec_options( ecsact_registry_id registry_id, ecsact_entity_id entity_id, [[maybe_unused]] ecsact_component_id component_id, - ... + const void* indexed_fields ) -> void { using ecsact::entt::detail::pending_remove; + static_assert( + !C::has_assoc_fields, + "Ecsact RT EnTT doesn't support indexed fields (yet)" + ); + auto& reg = ecsact::entt::get_registry(registry_id); auto entity = ecsact::entt::entity_id{entity_id}; assert(C::id == component_id); @@ -475,4 +506,25 @@ auto update_exec_itr_beforechange( beforechange_comp.value = comp; } +template +auto ecsact_stream( + ecsact_registry_id registry_id, + ecsact_entity_id entity_id, + [[maybe_unused]] ecsact_component_id component_id, + const void* component_data, + const void* indexed_fields +) -> ecsact_stream_error { + static_assert( + !C::has_assoc_fields, + "Ecsact RT EnTT doesn't support indexed fields (yet)" + ); + + auto component = static_cast(component_data); + + using ecsact::entt::detail::globals::stream_registries; + + stream_registries.handle_stream(registry_id, entity_id, *component); + + return ECSACT_STREAM_OK; +} } // namespace ecsact::entt::wrapper::core diff --git a/ecsact/entt/wrapper/dynamic.hh b/ecsact/entt/wrapper/dynamic.hh index f8a2e79..e924e70 100644 --- a/ecsact/entt/wrapper/dynamic.hh +++ b/ecsact/entt/wrapper/dynamic.hh @@ -70,8 +70,8 @@ template auto context_remove( ecsact_system_execution_context* context, [[maybe_unused]] ecsact_component_like_id component_id, - auto& view, - ... + const void* indexed_field_values, + auto& view ) -> void { assert(ecsact_id_cast(C::id) == component_id); @@ -126,8 +126,8 @@ auto context_get( ecsact_system_execution_context* context, [[maybe_unused]] ecsact_component_like_id component_id, void* out_component_data, - auto& view, - ... + const void* indexed_field_values, + auto& view ) -> void { auto entity = context->entity; @@ -139,6 +139,7 @@ auto context_update( ecsact_system_execution_context* context, [[maybe_unused]] ecsact_component_like_id component_id, const void* in_component_data, + const void* indexed_field_values, auto& view ) -> void { using ecsact::entt::detail::exec_beforechange_storage; @@ -161,23 +162,62 @@ template auto context_has( ecsact_system_execution_context* context, [[maybe_unused]] ecsact_component_like_id component_id, - ... + const void* indexed_fields ) -> bool { + static_assert( + !C::has_assoc_fields, + "Ecsact RT EnTT doesn't support indexed fields (yet)" + ); + auto entity = context->entity; auto& registry = *context->registry; return registry.template any_of(entity); } +template +auto context_stream_toggle( + ecsact_system_execution_context* context, + [[maybe_unused]] ecsact_component_like_id component_id, + bool streaming_enabled, + const void* indexed_fields +) { + using ecsact::entt::detail::run_on_stream; + + static_assert( + !C::has_assoc_fields, + "Ecsact RT EnTT doesn't support indexed fields (yet)" + ); + + auto entity = context->entity; + auto& registry = *context->registry; + + if(streaming_enabled) { + if(registry.any_of>(entity)) { + registry.template remove>(entity); + } + } else { + if(!registry.any_of>(entity)) { + registry.template emplace>(entity); + } + } +} + template auto context_generate_add( ecsact_system_execution_context* context, ecsact_component_id component_id, const void* component_data, + const void* indexed_fields, ecsact::entt::entity_id entity ) -> void { using ecsact::entt::detail::pending_add; + static_assert( + !C::has_assoc_fields, + "Ecsact RT EnTT doesn't support indexed fields (yet)" + ); + auto& registry = *context->registry; const auto& component = *static_cast(component_data); diff --git a/rt_entt_codegen/core/BUILD.bazel b/rt_entt_codegen/core/BUILD.bazel index cd4b655..c9ff17c 100644 --- a/rt_entt_codegen/core/BUILD.bazel +++ b/rt_entt_codegen/core/BUILD.bazel @@ -38,6 +38,10 @@ _CORE_CODEGEN_METHODS = { ], "check_error": [], "execution_options": [], + "apply_streaming_data": [ + "//rt_entt_codegen/shared:parallel", + "//rt_entt_codegen/shared:system_variant", + ], "sorting_components": [ "//rt_entt_codegen/shared:sorting", ], diff --git a/rt_entt_codegen/core/apply_streaming_data.cc b/rt_entt_codegen/core/apply_streaming_data.cc new file mode 100644 index 0000000..9f9ba70 --- /dev/null +++ b/rt_entt_codegen/core/apply_streaming_data.cc @@ -0,0 +1,46 @@ + +#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" +#include "ecsact/runtime/meta.h" + +auto ecsact::rt_entt_codegen::core::print_apply_streaming_data( + 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::meta::decl_full_name; + + block( + ctx, + "auto apply_streaming_data(::entt::registry& main_reg) -> void", + [&] { + ctx.write( + "auto stream_registries = " + "ecsact::entt::detail::globals::stream_registries.get_stream_" + "registries();\n" + ); + + block(ctx, "for(auto& stream_reg: stream_registries)", [&] { + for(auto component_id : details.all_components) { + auto comp_type = ecsact_meta_component_type( + ecsact_id_cast(component_id) + ); + + if(comp_type == ECSACT_COMPONENT_TYPE_LAZY_STREAM || + comp_type == ECSACT_COMPONENT_TYPE_STREAM) { + auto comp_name = cpp_identifier(decl_full_name(component_id)); + + ctx.write("ecsact::entt::detail::apply_component_stream_data<"); + ctx.write(comp_name); + ctx.write(">(main_reg, *stream_reg);\n"); + } + } + }); + } + ); +} diff --git a/rt_entt_codegen/core/core.hh b/rt_entt_codegen/core/core.hh index 532d98d..2bb8c90 100644 --- a/rt_entt_codegen/core/core.hh +++ b/rt_entt_codegen/core/core.hh @@ -91,4 +91,9 @@ auto print_update_all_beforechange_storage( const ecsact_entt_details& details ) -> void; +auto print_apply_streaming_data( + codegen_plugin_context& ctx, + const ecsact_entt_details& details +) -> void; + } // namespace ecsact::rt_entt_codegen::core diff --git a/rt_entt_codegen/core/execute_systems.cc b/rt_entt_codegen/core/execute_systems.cc index 618f970..b78e4b0 100644 --- a/rt_entt_codegen/core/execute_systems.cc +++ b/rt_entt_codegen/core/execute_systems.cc @@ -60,6 +60,8 @@ auto ecsact::rt_entt_codegen::core::print_execute_systems( // ctx.write("\n"); + ctx.write("apply_streaming_data(registry);\n"); + ctx.write("for(auto i=0; execution_count > i; ++i) {"); ctx.indentation += 1; ctx.write("\n"); @@ -78,6 +80,8 @@ auto ecsact::rt_entt_codegen::core::print_execute_systems( // }); }); + ctx.write("\n"); + std::vector system_like_variants; for(const auto sys_like_id : details.top_execution_order) { diff --git a/rt_entt_codegen/core/execution_options.cc b/rt_entt_codegen/core/execution_options.cc index 636e02d..5a16d1c 100644 --- a/rt_entt_codegen/core/execution_options.cc +++ b/rt_entt_codegen/core/execution_options.cc @@ -185,7 +185,7 @@ auto ecsact::rt_entt_codegen::core::print_execution_options( "execution_update_fns.at(ecsact_id_cast(" "component.component_id))(registry_id, " "ecsact::entt::entity_id(entity), " - "component.component_id, component.component_data);\n" + "component.component_id, component.component_data, nullptr);\n" ); }); @@ -197,7 +197,7 @@ auto ecsact::rt_entt_codegen::core::print_execution_options( "execution_remove_fns.at(ecsact_id_cast(" "component_id))(registry_id, " "ecsact::entt::entity_id(entity), " - "component_id);\n\n" + "component_id, nullptr);\n\n" ); }); diff --git a/rt_entt_codegen/core/print_sys_exec.cc b/rt_entt_codegen/core/print_sys_exec.cc index af94143..ee7f409 100644 --- a/rt_entt_codegen/core/print_sys_exec.cc +++ b/rt_entt_codegen/core/print_sys_exec.cc @@ -1,6 +1,5 @@ #include "core.hh" -#include #include #include #include @@ -95,6 +94,7 @@ static auto print_sys_exec_ctx_remove( auto printer = // method_printer{ctx, "remove"} .parameter("ecsact_component_like_id", "component_id") + .parameter("const void*", "indexed_fields") .return_type("void final"); auto result = std::ranges::find_if(system_providers, [&](auto provider) { @@ -116,6 +116,7 @@ static auto print_sys_exec_ctx_get( method_printer{ctx, "get"} .parameter("ecsact_component_like_id", "component_id") .parameter("void*", "out_component_data") + .parameter("const void*", "indexed_fields") .return_type("void final"); auto result = std::ranges::find_if(system_providers, [&](auto provider) { @@ -137,6 +138,7 @@ static auto print_sys_exec_ctx_update( method_printer{ctx, "update"} .parameter("ecsact_component_like_id", "component_id") .parameter("const void*", "component_data") + .parameter("const void*", "indexed_fields") .return_type("void final"); auto result = std::ranges::find_if(system_providers, [&](auto provider) { @@ -157,6 +159,7 @@ static auto print_sys_exec_ctx_has( auto printer = // method_printer{ctx, "has"} .parameter("ecsact_component_like_id", "component_id") + .parameter("const void*", "indexed_fields") .return_type("bool final"); auto result = std::ranges::find_if(system_providers, [&](auto provider) { @@ -230,6 +233,28 @@ auto print_sys_exec_ctx_other( } } +auto print_sys_exec_ctx_stream_toggle( + ecsact::codegen_plugin_context& ctx, + const common_vars names, + system_provider_t system_providers +) -> void { + auto printer = // + method_printer{ctx, "stream_toggle"} + .parameter("ecsact_component_id", "component_id") + .parameter("bool", "streaming_enabled") + .parameter("const void*", "indexed_fields") + .return_type("void"); + + auto result = std::ranges::find_if(system_providers, [&](auto provider) { + return provider->context_function_stream_toggle(ctx, names) == + handle_exclusive_provide::HANDLED; + }); + + if(result == system_providers.end()) { + ctx.fatal("INTERNAL: print context toggle was not handled by providers"); + } +} + template static auto print_apply_pendings( ecsact::codegen_plugin_context& ctx, @@ -315,6 +340,7 @@ static auto print_system_execution_context( print_sys_exec_ctx_generate(ctx, names, system_providers); print_sys_exec_ctx_parent(ctx, names, system_providers); print_sys_exec_ctx_other(ctx, names, system_providers); + print_sys_exec_ctx_stream_toggle(ctx, names, system_providers); }); ctx.write(";\n\n"); @@ -365,6 +391,36 @@ static auto setup_system_providers(system_like_id_variant sys_like_id return system_providers; } +static auto add_stream_component_if_needed( + ecsact::codegen_plugin_context& ctx, + system_like_id_variant sys_like_id, + std::vector& additional_view_components +) -> void { + using ecsact::cc_lang_support::cpp_identifier; + using ecsact::meta::decl_full_name; + + auto sys_details = ecsact_entt_system_details::from_system_like(sys_like_id); + + auto comp_caps = ecsact::meta::system_capabilities(sys_like_id); + for(auto [comp_id, capability] : comp_caps) { + auto comp_type = ecsact_meta_component_type(comp_id); + + if(comp_type != ECSACT_COMPONENT_TYPE_STREAM && + comp_type != ECSACT_COMPONENT_TYPE_LAZY_STREAM) { + continue; + } + + if(capability == ECSACT_SYS_CAP_STREAM_TOGGLE) { + continue; + } + + auto comp_ident = cpp_identifier(decl_full_name(comp_id)); + auto run_on_stream_str = + std::format("::ecsact::entt::detail::run_on_stream<{}>", comp_ident); + additional_view_components.push_back(run_on_stream_str); + } +} + static auto print_execute_systems( ecsact::codegen_plugin_context& ctx, system_like_id_variant sys_like_id, @@ -384,6 +440,10 @@ static auto print_execute_systems( auto sys_details = ecsact_entt_system_details::from_system_like(sys_like_id); + auto system_name = cpp_identifier(decl_full_name(sys_like_id)); + + add_stream_component_if_needed(ctx, sys_like_id, additional_view_components); + ecsact::rt_entt_codegen::util::make_view( ctx, "view", diff --git a/rt_entt_codegen/core/system_provider/basic/basic.cc b/rt_entt_codegen/core/system_provider/basic/basic.cc index 344f3ee..bdb9461 100644 --- a/rt_entt_codegen/core/system_provider/basic/basic.cc +++ b/rt_entt_codegen/core/system_provider/basic/basic.cc @@ -102,6 +102,14 @@ auto provider::basic::context_function_other( return HANDLED; } +auto provider::basic::context_function_stream_toggle( + ecsact::codegen_plugin_context& ctx, + const common_vars& names +) -> handle_exclusive_provide { + context_stream_toggle_impl(ctx, sys_like_id, system_details); + return HANDLED; +} + auto provider::basic::system_impl( ecsact::codegen_plugin_context& ctx, const common_vars& names diff --git a/rt_entt_codegen/core/system_provider/basic/basic.hh b/rt_entt_codegen/core/system_provider/basic/basic.hh index 3163071..82fe7d0 100644 --- a/rt_entt_codegen/core/system_provider/basic/basic.hh +++ b/rt_entt_codegen/core/system_provider/basic/basic.hh @@ -66,6 +66,11 @@ public: const common_vars& names ) -> handle_exclusive_provide final; + [[nodiscard]] virtual auto context_function_stream_toggle( + ecsact::codegen_plugin_context& ctx, + const ecsact::rt_entt_codegen::core::common_vars& names + ) -> handle_exclusive_provide; + auto entity_iteration( ecsact::codegen_plugin_context& ctx, const ecsact::rt_entt_codegen::core::common_vars& names, diff --git a/rt_entt_codegen/core/system_provider/system_ctx_functions.cc b/rt_entt_codegen/core/system_provider/system_ctx_functions.cc index 8844c7d..9976135 100644 --- a/rt_entt_codegen/core/system_provider/system_ctx_functions.cc +++ b/rt_entt_codegen/core/system_provider/system_ctx_functions.cc @@ -118,13 +118,13 @@ auto ecsact::rt_entt_codegen::core::provider::context_remove_impl( type_name, ">(this, ecsact_id_cast(", type_name, - "::id), *view);\n" + "::id), nullptr, *view);\n" ); return; } ctx.write(std::format( "using remove_fn_t = void (*)(ecsact_system_execution_context*, " - "ecsact_component_like_id, {}_t&);\n", + "ecsact_component_like_id, const void*, {}_t&);\n", view_type_name )); @@ -149,6 +149,8 @@ auto ecsact::rt_entt_codegen::core::provider::context_remove_impl( ctx.write("return result;\n"); }); ctx.write("();\n"); + ctx.write("remove_fns.at(component_id)(this, component_id, nullptr, *view);\n" + ); } auto ecsact::rt_entt_codegen::core::provider::context_get_impl( @@ -196,7 +198,7 @@ auto ecsact::rt_entt_codegen::core::provider::context_get_impl( ctx.write(std::format( "using get_fn_t = void (*)(ecsact_system_execution_context*, " - "ecsact_component_like_id, void *, {}_t&, ...);\n", + "ecsact_component_like_id, void *, const void*, {}_t&);\n", view_type_name )); @@ -223,7 +225,9 @@ auto ecsact::rt_entt_codegen::core::provider::context_get_impl( ctx.write("();\n"); ctx.write( - "get_fns.at(component_id)(this, component_id, out_component_data, *view);\n" + "get_fns.at(" + "component_id)(this, component_id, out_component_data, nullptr, *view" + ");\n" ); } @@ -246,14 +250,14 @@ auto ecsact::rt_entt_codegen::core::provider::context_update_impl( ">(this, ecsact_id_cast(", type_name, "::id),", - "component_data, *view); \n" + "component_data, nullptr, *view); \n" ); return; } ctx.write(std::format( "using update_fn_t = void (*)(ecsact_system_execution_context*, " - "ecsact_component_like_id, const void *, {}_t&);\n", + "ecsact_component_like_id, const void *, const void*, {}_t&);\n", view_type_name )); @@ -280,7 +284,7 @@ auto ecsact::rt_entt_codegen::core::provider::context_update_impl( ctx.write("();\n"); ctx.write( - "update_fns.at(component_id)(this, component_id, component_data, " + "update_fns.at(component_id)(this, component_id, component_data, nullptr, " "*view);\n" ); } @@ -303,7 +307,7 @@ auto ecsact::rt_entt_codegen::core::provider::context_has_impl( type_name, ">(this, ecsact_id_cast(", type_name, - "::id));\n" + "::id), nullptr);\n" ); } block( @@ -326,7 +330,7 @@ auto ecsact::rt_entt_codegen::core::provider::context_has_impl( ); ctx.write(";\n"); - ctx.write("return has_fns.at(component_id)(this, component_id);\n"); + ctx.write("return has_fns.at(component_id)(this, component_id, nullptr);\n"); } auto ecsact::rt_entt_codegen::core::provider::context_generate_impl( @@ -343,7 +347,7 @@ auto ecsact::rt_entt_codegen::core::provider::context_generate_impl( "static const auto generate_fns = " "std::unordered_map", + "void*, const void*, ecsact::entt::entity_id)>", [&] { for(const auto& component : details.generate_comps) { for(const auto& [comp_id, requirements] : component) { @@ -377,11 +381,54 @@ auto ecsact::rt_entt_codegen::core::provider::context_generate_impl( ctx.write( "generate_fns.at(component_id)(this, component_id, " - "component_data, entity);\n" + "component_data, nullptr, entity);\n" ); }); } +auto ecsact::rt_entt_codegen::core::provider::context_stream_toggle_impl( + ecsact::codegen_plugin_context& ctx, + const system_like_id_variant& sys_like_id, + const ecsact::rt_entt_codegen::ecsact_entt_system_details& details +) -> void { + auto stream_comps = details.stream_comps; + if(stream_comps.empty()) { + // TODO(Kelwan): Handle unexpected behaviour + return; + } else if(stream_comps.size() == 1) { + const auto& comp_id = stream_comps.begin(); + auto type_name = cpp_identifier(decl_full_name(*comp_id)); + ctx.write( + "wrapper::dynamic::context_stream_toggle<::", + type_name, + ">(this, ecsact_id_cast(", + type_name, + "::id), streaming_enabled, nullptr); \n" + ); + return; + } + block( + ctx, + "static const auto toggle_fns = " + "std::unordered_map", + [&] { + for(const auto comp_id : stream_comps) { + auto type_name = cpp_identifier(decl_full_name(comp_id)); + ctx.write( + "{", + "ecsact_id_cast(", + type_name, + "::id), ", + "&wrapper::dynamic::context_stream_toggle<::", + type_name, + "> }," + ); + } + } + ); +} + auto ecsact::rt_entt_codegen::core::provider::context_parent_impl( ecsact::codegen_plugin_context& ctx, const system_like_id_variant& sys_like_id diff --git a/rt_entt_codegen/core/system_provider/system_ctx_functions.hh b/rt_entt_codegen/core/system_provider/system_ctx_functions.hh index 8d1005c..c87836c 100644 --- a/rt_entt_codegen/core/system_provider/system_ctx_functions.hh +++ b/rt_entt_codegen/core/system_provider/system_ctx_functions.hh @@ -44,6 +44,11 @@ auto context_generate_impl( const system_like_id_variant& sys_like_id, const ecsact::rt_entt_codegen::ecsact_entt_system_details& details ) -> void; +auto context_stream_toggle_impl( + ecsact::codegen_plugin_context& ctx, + const system_like_id_variant& sys_like_id, + const ecsact::rt_entt_codegen::ecsact_entt_system_details& details +) -> void; auto context_parent_impl( ecsact::codegen_plugin_context& ctx, const system_like_id_variant& sys_like_id diff --git a/rt_entt_codegen/core/system_provider/system_provider.cc b/rt_entt_codegen/core/system_provider/system_provider.cc index 170bc3c..440e3e0 100644 --- a/rt_entt_codegen/core/system_provider/system_provider.cc +++ b/rt_entt_codegen/core/system_provider/system_provider.cc @@ -99,6 +99,13 @@ auto system_provider::context_function_other( return NOT_HANDLED; } +auto system_provider::context_function_stream_toggle( + ecsact::codegen_plugin_context& ctx, + const common_vars& names +) -> handle_exclusive_provide { + return NOT_HANDLED; +} + auto system_provider::pre_entity_iteration( ecsact::codegen_plugin_context& ctx, const common_vars& names diff --git a/rt_entt_codegen/core/system_provider/system_provider.hh b/rt_entt_codegen/core/system_provider/system_provider.hh index 7918d87..6e54385 100644 --- a/rt_entt_codegen/core/system_provider/system_provider.hh +++ b/rt_entt_codegen/core/system_provider/system_provider.hh @@ -77,6 +77,10 @@ public: ecsact::codegen_plugin_context& ctx, const ecsact::rt_entt_codegen::core::common_vars& names ) -> handle_exclusive_provide; + [[nodiscard]] virtual auto context_function_stream_toggle( + ecsact::codegen_plugin_context& ctx, + const ecsact::rt_entt_codegen::core::common_vars& names + ) -> handle_exclusive_provide; virtual auto pre_entity_iteration( ecsact::codegen_plugin_context& ctx, diff --git a/rt_entt_codegen/rt_entt_codegen.cc b/rt_entt_codegen/rt_entt_codegen.cc index 4da700d..0c5ee12 100644 --- a/rt_entt_codegen/rt_entt_codegen.cc +++ b/rt_entt_codegen/rt_entt_codegen.cc @@ -2,6 +2,7 @@ #include #include "core/core.hh" #include "ecsact/runtime/meta.hh" +#include "ecsact/runtime/meta.h" #include "ecsact/codegen/plugin.h" #include "ecsact/codegen/plugin.hh" #include "ecsact/lang-support/lang-cc.hh" @@ -132,7 +133,7 @@ void ecsact_codegen_plugin( inc_header(ctx, "ecsact/entt/detail/apply_pending.hh"); inc_header(ctx, "ecsact/entt/detail/registry.hh"); inc_header(ctx, "ecsact/entt/detail/bytes.hh"); - inc_header(ctx, "ecsact/entt/detail/hash.hh"); + inc_header(ctx, "ecsact/entt/detail/apply_component_stream_data.hh"); inc_header(ctx, "ecsact/entt/detail/hash.hh"); inc_header(ctx, "ecsact/entt/wrapper/core.hh"); inc_header(ctx, "ecsact/entt/wrapper/dynamic.hh"); @@ -284,6 +285,39 @@ void ecsact_codegen_plugin( } }); + init_global(ctx, "ecsact_stream_fns", [&] { + if(details.all_components.empty()) { + return; + } + + std::unordered_set stream_components; + + for(auto comp_id : details.all_components) { + auto comp_type = ecsact_meta_component_type( + ecsact_id_cast(comp_id) + ); + + if(comp_type == ECSACT_COMPONENT_TYPE_STREAM || + comp_type == ECSACT_COMPONENT_TYPE_LAZY_STREAM) { + stream_components.insert(comp_id); + } + } + + ctx.write("result.reserve(", stream_components.size(), ");\n"); + + for(auto comp_id : stream_components) { + auto cpp_comp_name = cpp_identifier(decl_full_name(comp_id)); + ctx.write( + "result.insert({::", + cpp_comp_name, + "::id, ", + "&ecsact::entt::wrapper::core::ecsact_stream<::", + cpp_comp_name, + ">});\n" + ); + } + }); + ctx.write("\n"); for(auto comp_id : details.all_components) { @@ -317,6 +351,7 @@ void ecsact_codegen_plugin( core::print_execute_system_like_template_specializations(ctx, details); core::print_init_registry_storage(ctx, details); core::print_create_registry(ctx, details); + core::print_apply_streaming_data(ctx, details); core::print_trigger_ecsact_events_minimal(ctx, details); core::print_trigger_ecsact_events_all(ctx, details); core::print_cleanup_ecsact_component_events(ctx, details); diff --git a/rt_entt_codegen/shared/ecsact_entt_details.cc b/rt_entt_codegen/shared/ecsact_entt_details.cc index 28a1cd9..146b14b 100644 --- a/rt_entt_codegen/shared/ecsact_entt_details.cc +++ b/rt_entt_codegen/shared/ecsact_entt_details.cc @@ -120,6 +120,9 @@ auto ecsact_entt_system_details::fill_system_details( out_details.exclude_comps.insert(comp_id); out_details.addable_comps.insert(comp_id); break; + case ECSACT_SYS_CAP_STREAM_TOGGLE: + out_details.get_comps.insert(comp_id); + out_details.stream_comps.insert(comp_id); } } diff --git a/rt_entt_codegen/shared/ecsact_entt_details.hh b/rt_entt_codegen/shared/ecsact_entt_details.hh index a53fc51..f8d4a99 100644 --- a/rt_entt_codegen/shared/ecsact_entt_details.hh +++ b/rt_entt_codegen/shared/ecsact_entt_details.hh @@ -36,6 +36,9 @@ struct ecsact_entt_system_details { /** Components this system is allowed to remove */ std::unordered_set removable_comps; + /** Streaming components on the system */ + std::unordered_set stream_comps; + /** A map containing this system's generated component ids and its * requirements*/ generate_t generate_comps; diff --git a/rt_entt_codegen/shared/parallel.cc b/rt_entt_codegen/shared/parallel.cc index 3b68f4a..d63c6e4 100644 --- a/rt_entt_codegen/shared/parallel.cc +++ b/rt_entt_codegen/shared/parallel.cc @@ -32,7 +32,8 @@ auto ecsact::rt_entt_codegen::parallel::get_parallel_execution_cluster( static auto is_capability_safe(ecsact_system_capability capability) -> bool { std::underlying_type_t unsafe_caps = - ECSACT_SYS_CAP_ADDS | ECSACT_SYS_CAP_REMOVES | ECSACT_SYS_CAP_WRITEONLY; + ECSACT_SYS_CAP_ADDS | ECSACT_SYS_CAP_REMOVES | ECSACT_SYS_CAP_WRITEONLY | + ECSACT_SYS_CAP_STREAM_TOGGLE; unsafe_caps &= ~(ECSACT_SYS_CAP_EXCLUDE | ECSACT_SYS_CAP_INCLUDE); return (unsafe_caps & capability) == 0b0; @@ -112,7 +113,7 @@ static auto is_capability_safe_entities( } std::underlying_type_t unsafe_caps = - ECSACT_SYS_CAP_ADDS | ECSACT_SYS_CAP_REMOVES; + ECSACT_SYS_CAP_ADDS | ECSACT_SYS_CAP_REMOVES | ECSACT_SYS_CAP_STREAM_TOGGLE; unsafe_caps &= ~(ECSACT_SYS_CAP_EXCLUDE | ECSACT_SYS_CAP_INCLUDE); return (unsafe_caps & capability) == 0b0; diff --git a/rt_entt_codegen/shared/system_util.cc b/rt_entt_codegen/shared/system_util.cc index 1dc8da3..b9840a2 100644 --- a/rt_entt_codegen/shared/system_util.cc +++ b/rt_entt_codegen/shared/system_util.cc @@ -44,6 +44,7 @@ auto ecsact::rt_entt_codegen::system_util::is_trivial_system( ECSACT_SYS_CAP_OPTIONAL_READONLY, ECSACT_SYS_CAP_OPTIONAL_READWRITE, ECSACT_SYS_CAP_OPTIONAL_WRITEONLY, + ECSACT_SYS_CAP_STREAM_TOGGLE, }; bool has_non_tag_adds = false; diff --git a/runtime/BUILD.bazel b/runtime/BUILD.bazel index 672b2d9..f97e104 100644 --- a/runtime/BUILD.bazel +++ b/runtime/BUILD.bazel @@ -4,6 +4,7 @@ package(default_visibility = ["//visibility:public"]) _srcs = [ "ecsact_rt_entt_core.cc", "ecsact_rt_entt_dynamic.cc", + "stream_registries.cc", "hash.cc", ] diff --git a/runtime/ecsact_rt_entt_core.cc b/runtime/ecsact_rt_entt_core.cc index 7dea449..de5d9b7 100644 --- a/runtime/ecsact_rt_entt_core.cc +++ b/runtime/ecsact_rt_entt_core.cc @@ -110,24 +110,24 @@ bool ecsact_has_component( ecsact_registry_id reg_id, ecsact_entity_id entity_id, ecsact_component_id component_id, - ... + const void* indexed_fields ) { using ecsact::entt::detail::globals::has_component_fns; auto fn_itr = has_component_fns.find(component_id); assert(fn_itr != has_component_fns.end()); - return fn_itr->second(reg_id, entity_id, component_id); + return fn_itr->second(reg_id, entity_id, component_id, indexed_fields); } const void* ecsact_get_component( ecsact_registry_id reg_id, ecsact_entity_id entity_id, ecsact_component_id component_id, - ... + const void* indexed_fields ) { using ecsact::entt::detail::globals::get_component_fns; auto fn_itr = get_component_fns.find(component_id); assert(fn_itr != get_component_fns.end()); - return fn_itr->second(reg_id, entity_id, component_id); + return fn_itr->second(reg_id, entity_id, component_id, indexed_fields); } int ecsact_count_components( @@ -138,7 +138,7 @@ int ecsact_count_components( int component_count = 0; for(auto comp_id : all_component_ids) { - if(ecsact_has_component(registry_id, entity_id, comp_id)) { + if(ecsact_has_component(registry_id, entity_id, comp_id, nullptr)) { component_count += 1; } } @@ -154,10 +154,10 @@ void ecsact_each_component( using ecsact::entt::detail::globals::all_component_ids; for(auto comp_id : all_component_ids) { - if(ecsact_has_component(registry_id, entity_id, comp_id)) { + if(ecsact_has_component(registry_id, entity_id, comp_id, nullptr)) { callback( comp_id, - ecsact_get_component(registry_id, entity_id, comp_id), + ecsact_get_component(registry_id, entity_id, comp_id, nullptr), callback_user_data ); } @@ -180,10 +180,10 @@ void ecsact_get_components( break; } - if(ecsact_has_component(registry_id, entity_id, comp_id)) { + if(ecsact_has_component(registry_id, entity_id, comp_id, nullptr)) { out_component_ids[index] = comp_id; out_components_data[index] = - ecsact_get_component(registry_id, entity_id, comp_id); + ecsact_get_component(registry_id, entity_id, comp_id, nullptr); index += 1; } } @@ -198,22 +198,43 @@ ecsact_update_error ecsact_update_component( ecsact_entity_id entity_id, ecsact_component_id component_id, const void* component_data, - ... + const void* indexed_fields ) { using ecsact::entt::detail::globals::update_component_fns; auto fn_itr = update_component_fns.find(component_id); assert(fn_itr != update_component_fns.end()); - return fn_itr->second(reg_id, entity_id, component_id, component_data); + return fn_itr + ->second(reg_id, entity_id, component_id, component_data, indexed_fields); } void ecsact_remove_component( ecsact_registry_id reg_id, ecsact_entity_id entity_id, ecsact_component_id component_id, - ... + const void* indexed_fields ) { using ecsact::entt::detail::globals::remove_component_fns; auto fn_itr = remove_component_fns.find(component_id); assert(fn_itr != remove_component_fns.end()); - return fn_itr->second(reg_id, entity_id, component_id); + return fn_itr->second(reg_id, entity_id, component_id, indexed_fields); +} + +ecsact_stream_error ecsact_stream( + ecsact_registry_id reg_id, + ecsact_entity_id entity_id, + ecsact_component_id component_id, + const void* component_data, + const void* indexed_fields +) { + using ecsact::entt::detail::globals::ecsact_stream_fns; + auto fn_itr = ecsact_stream_fns.find(component_id); + assert(fn_itr != ecsact_stream_fns.end()); + + return fn_itr->second( // + reg_id, + entity_id, + component_id, + component_data, + indexed_fields + ); } diff --git a/runtime/ecsact_rt_entt_dynamic.cc b/runtime/ecsact_rt_entt_dynamic.cc index f334e0b..6b91bef 100644 --- a/runtime/ecsact_rt_entt_dynamic.cc +++ b/runtime/ecsact_rt_entt_dynamic.cc @@ -78,39 +78,39 @@ void ecsact_system_execution_context_add( void ecsact_system_execution_context_remove( ecsact_system_execution_context* context, ecsact_component_like_id comp_id, - ... + const void* indexed_fields ) { assert(context != nullptr); - return context->remove(comp_id); + return context->remove(comp_id, indexed_fields); } void ecsact_system_execution_context_get( ecsact_system_execution_context* context, ecsact_component_like_id comp_id, void* out_component_data, - ... + const void* indexed_fields ) { assert(context != nullptr); - return context->get(comp_id, out_component_data); + return context->get(comp_id, out_component_data, indexed_fields); } void ecsact_system_execution_context_update( ecsact_system_execution_context* context, ecsact_component_like_id comp_id, const void* component_data, - ... + const void* indexed_fields ) { assert(context != nullptr); - return context->update(comp_id, component_data); + return context->update(comp_id, component_data, indexed_fields); } bool ecsact_system_execution_context_has( ecsact_system_execution_context* context, ecsact_component_like_id comp_id, - ... + const void* indexed_fields ) { assert(context != nullptr); - return context->has(comp_id); + return context->has(comp_id, indexed_fields); } void ecsact_system_execution_context_action( @@ -121,6 +121,16 @@ void ecsact_system_execution_context_action( return context->action(out_action_data); } +void ecsact_system_execution_context_stream_toggle( + ecsact_system_execution_context* context, + ecsact_component_id comp_id, + bool streaming_enabled, + const void* indexed_fields +) { + assert(context != nullptr); + return context->stream_toggle(comp_id, streaming_enabled, indexed_fields); +} + const ecsact_system_execution_context* ecsact_system_execution_context_parent( ecsact_system_execution_context* context ) { diff --git a/runtime/stream_registries.cc b/runtime/stream_registries.cc new file mode 100644 index 0000000..5c4cd12 --- /dev/null +++ b/runtime/stream_registries.cc @@ -0,0 +1,44 @@ +#include + +#include + +#include "ecsact/entt/stream_registries.hh" +#include "ecsact/entt/detail/globals.hh" + +ecsact::entt::stream::stream_registries + ecsact::entt::detail::globals::stream_registries = {}; + +auto ecsact::entt::stream::stream_registries::get_stream_registries() + -> std::vector> { + auto registries = std::vector>{}; + + std::unique_lock lk(stream_mutex); + for(auto&& [reg_id, map] : registry_stream_threads) { + for(auto&& [thread_id, registry] : map) { + if(!registry->empty()) { + registries.push_back(std::move(registry)); + registry = std::make_unique<::entt::registry>(); + } else { + // TODO(Kelwan): Clean up hanging threads here? + } + } + } + return registries; +} + +auto ecsact::entt::stream::stream_registries::add_registry( + ecsact_registry_id registry_id +) -> void { + auto thread_map = detail::child_reg_thread_map{}; + + // thread_map.emplace_hint( + // thread_map.end(), + // std::pair(thread_id, std::make_unique<::entt::registry>()) + // ); + + std::unique_lock lk(stream_mutex); + registry_stream_threads.insert( + registry_stream_threads.end(), + std::pair(registry_id, std::move(thread_map)) + ); +} diff --git a/test/MODULE.bazel b/test/MODULE.bazel index ae1239b..dae87ec 100644 --- a/test/MODULE.bazel +++ b/test/MODULE.bazel @@ -3,13 +3,13 @@ module(name = "ecsact_rt_entt_test") bazel_dep(name = "rules_cc", version = "0.0.9") bazel_dep(name = "bazel_skylib", version = "1.6.1") bazel_dep(name = "googletest", version = "1.14.0.bcr.1") -bazel_dep(name = "rules_ecsact", version = "0.5.6") -bazel_dep(name = "ecsact_lang_cpp", version = "0.4.6") -bazel_dep(name = "ecsact_runtime", version = "0.6.7") +bazel_dep(name = "rules_ecsact", version = "0.5.8") +bazel_dep(name = "ecsact_lang_cpp", version = "0.4.10") +bazel_dep(name = "ecsact_runtime", version = "0.7.0") bazel_dep(name = "boost.mp11", version = "1.83.0.bzl.1") bazel_dep(name = "boost.dll", version = "1.83.0.bzl.2") bazel_dep(name = "entt", version = "3.12.2") -bazel_dep(name = "ecsact_cli", version = "0.3.16") +bazel_dep(name = "ecsact_cli", version = "0.3.19") bazel_dep(name = "boost.process", version = "1.83.0.bzl.2") bazel_dep(name = "toolchains_llvm", version = "1.0.0", dev_dependency = True) diff --git a/test/runtime_test.cc b/test/runtime_test.cc index 4a43896..3a53dc8 100644 --- a/test/runtime_test.cc +++ b/test/runtime_test.cc @@ -3,10 +3,11 @@ #include #include #include -#include #include #include +#include #include +#include #include "ecsact/runtime/core.hh" #include "ecsact/runtime/dynamic.h" @@ -168,6 +169,25 @@ void runtime_test::MixedNotify::impl(context& ctx) { ctx.update(comp); } +void runtime_test::StreamTestSystem::impl(context& ctx) { + auto comp = ctx.get(); + + if(comp.val == 0) { + ctx.stream_toggle(false); + } + + if(comp.val == 10) { + ctx.stream_toggle(true); + } +} + +void runtime_test::StreamTestSystemCounter::impl(context& ctx) { + auto toggle_comp = ctx.get(); + + toggle_comp.val += 10; + ctx.update(toggle_comp); +} + TEST(Core, CreateRegistry) { auto reg_id = ecsact_create_registry("CreateRegistry"); EXPECT_NE(reg_id, ecsact_invalid_registry_id); @@ -252,7 +272,7 @@ TEST(Core, AddComponent) { auto add_err = ecsact_add_component(reg_id, entity, comp_id, &comp); EXPECT_EQ(add_err, ECSACT_ADD_OK); - EXPECT_TRUE(ecsact_has_component(reg_id, entity, comp_id)); + EXPECT_TRUE(ecsact_has_component(reg_id, entity, comp_id, nullptr)); } TEST(Core, HasComponent) { @@ -262,9 +282,9 @@ TEST(Core, HasComponent) { runtime_test::ComponentA comp{.a = 42}; auto comp_id = static_cast(runtime_test::ComponentA::id); - EXPECT_FALSE(ecsact_has_component(reg_id, entity, comp_id)); + EXPECT_FALSE(ecsact_has_component(reg_id, entity, comp_id, nullptr)); ecsact_add_component(reg_id, entity, comp_id, &comp); - EXPECT_TRUE(ecsact_has_component(reg_id, entity, comp_id)); + EXPECT_TRUE(ecsact_has_component(reg_id, entity, comp_id, nullptr)); } TEST(Core, GetComponent) { @@ -276,7 +296,7 @@ TEST(Core, GetComponent) { ecsact_add_component(reg_id, entity, comp_id, &comp); auto comp_get = static_cast( - ecsact_get_component(reg_id, entity, comp_id) + ecsact_get_component(reg_id, entity, comp_id, nullptr) ); EXPECT_EQ(*comp_get, comp); @@ -290,10 +310,10 @@ TEST(Core, UpdateComponent) { runtime_test::ComponentA upped_comp{.a = 43}; auto comp_id = static_cast(runtime_test::ComponentA::id); ecsact_add_component(reg_id, entity, comp_id, &comp); - ecsact_update_component(reg_id, entity, comp_id, &upped_comp); + ecsact_update_component(reg_id, entity, comp_id, &upped_comp, nullptr); auto comp_get = static_cast( - ecsact_get_component(reg_id, entity, comp_id) + ecsact_get_component(reg_id, entity, comp_id, nullptr) ); EXPECT_EQ(*comp_get, upped_comp); @@ -306,9 +326,9 @@ TEST(Core, RemoveComponent) { runtime_test::ComponentA comp{.a = 42}; auto comp_id = static_cast(runtime_test::ComponentA::id); ecsact_add_component(reg_id, entity, comp_id, &comp); - EXPECT_TRUE(ecsact_has_component(reg_id, entity, comp_id)); - ecsact_remove_component(reg_id, entity, comp_id); - EXPECT_FALSE(ecsact_has_component(reg_id, entity, comp_id)); + EXPECT_TRUE(ecsact_has_component(reg_id, entity, comp_id, nullptr)); + ecsact_remove_component(reg_id, entity, comp_id, nullptr); + EXPECT_FALSE(ecsact_has_component(reg_id, entity, comp_id, nullptr)); } TEST(Core, TrivialRemoveEvent) { @@ -924,7 +944,7 @@ TEST(Core, StaticSystemImpl) { ecsact_add_component(reg_id, entity, comp_id, &comp); auto comp_get = static_cast( - ecsact_get_component(reg_id, entity, runtime_test::ComponentA::id) + ecsact_get_component(reg_id, entity, runtime_test::ComponentA::id, nullptr) ); // Sanity check @@ -940,7 +960,7 @@ TEST(Core, StaticSystemImpl) { ecsact_execute_systems(reg_id, 1, nullptr, nullptr); comp_get = static_cast( - ecsact_get_component(reg_id, entity, comp_id) + ecsact_get_component(reg_id, entity, comp_id, nullptr) ); EXPECT_NE(comp_get->a, comp.a); @@ -1287,3 +1307,151 @@ TEST(Core, NotifyMixed) { counter = reg.get_component(entity); ASSERT_EQ(counter.val, 3); } + +TEST(Core, StreamComponent) { + using runtime_test::StreamTest; + + auto reg = ecsact::core::registry("Stream"); + auto entity = reg.create_entity(); + auto exec_options = ecsact::core::execution_options{}; + + auto stream_component = StreamTest{.val = 0}; + + exec_options.add_component(entity, &stream_component); + + auto error = reg.execute_systems(std::array{exec_options}); + int prev_val = 0; + + for(int i = 0; i < 100; i++) { + stream_component.val += 10; + ecsact_stream(reg.id(), entity, StreamTest::id, &stream_component, nullptr); + reg.execute_systems(); + + stream_component = reg.get_component(entity); + ASSERT_EQ(stream_component.val, prev_val + 10); + prev_val = stream_component.val; + } +} + +TEST(Core, StreamComponentMultiThreadedOneEntity) { + using runtime_test::StreamTest; + + auto reg = ecsact::core::registry("Stream"); + auto entity = reg.create_entity(); + auto exec_options = ecsact::core::execution_options{}; + + auto thread_pool = std::array{}; + + auto stream_component = StreamTest{.val = 0}; + + exec_options.add_component(entity, &stream_component); + + auto error = reg.execute_systems(std::array{exec_options}); + int prev_val = 0; + + for(auto& thread : thread_pool) { + thread = std::thread([&, reg_id = reg.id(), entity] { + auto stream_component = StreamTest{.val = 0}; + for(int i = 0; i < 10; ++i) { + ecsact_stream( + reg_id, + entity, + StreamTest::id, + &stream_component, + nullptr + ); + } + }); + } + + for(int i = 0; i < 5; i++) { + reg.execute_systems(); + } + + for(auto& thread : thread_pool) { + thread.join(); + } +} + +TEST(Core, StreamComponentToggle) { + using runtime_test::StreamTestCounter; + using runtime_test::StreamTestToggle; + + ecsact_set_system_execution_impl( + ecsact_id_cast(runtime_test::StreamTestSystem::id), + runtime_test__StreamTestSystem + ); + + ecsact_set_system_execution_impl( + ecsact_id_cast( + runtime_test::StreamTestSystemCounter::id + ), + runtime_test__StreamTestSystemCounter + ); + + auto reg = ecsact::core::registry("Stream"); + auto entity = reg.create_entity(); + auto exec_options = ecsact::core::execution_options{}; + + auto stream_component = StreamTestToggle{.val = 0}; + auto stream_comp_counter = StreamTestCounter{.val = 0}; + + exec_options.add_component(entity, &stream_component); + exec_options.add_component(entity, &stream_comp_counter); + + auto error = reg.execute_systems(std::array{exec_options}); + int prev_val = 10; + + for(int i = 0; i < 5; i++) { + stream_component.val += 10; + ecsact_stream( + reg.id(), + entity, + StreamTestToggle::id, + &stream_component, + nullptr + ); + + reg.execute_systems(); + + stream_component = reg.get_component(entity); + stream_comp_counter = reg.get_component(entity); + ASSERT_EQ(stream_component.val, prev_val + 10); + prev_val = stream_component.val; + } + + stream_comp_counter.val += 10; + exec_options.clear(); + exec_options.update_component(entity, &stream_comp_counter); + error = reg.execute_systems(std::array{exec_options}); + + for(int i = 0; i < 5; i++) { + stream_component.val += 10; + ecsact_stream( + reg.id(), + entity, + StreamTestToggle::id, + &stream_component, + nullptr + ); + + reg.execute_systems(); + + stream_component = reg.get_component(entity); + stream_comp_counter = reg.get_component(entity); + ASSERT_EQ(stream_component.val, prev_val + 10); + prev_val = stream_component.val; + } + + ecsact_set_system_execution_impl( + ecsact_id_cast(runtime_test::StreamTestSystem::id), + nullptr + ); + + ecsact_set_system_execution_impl( + ecsact_id_cast( + runtime_test::StreamTestSystemCounter::id + ), + nullptr + ); +} diff --git a/test/runtime_test.ecsact b/test/runtime_test.ecsact index b92a30a..f1c2ab7 100644 --- a/test/runtime_test.ecsact +++ b/test/runtime_test.ecsact @@ -205,3 +205,25 @@ system MixedNotify { onremove NotifyComponentB; } } + +component StreamTest(stream) { + i32 val; +} + + +component StreamTestToggle(stream) { + i32 val; +} + +component StreamTestCounter { + i32 val; +} + +system StreamTestSystem { + stream_toggle StreamTestToggle; + readonly StreamTestCounter; +} + +system StreamTestSystemCounter { + readwrite StreamTestToggle; +}