diff --git a/.editorconfig b/.editorconfig index 0b3779e..8fb9453 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,3 +3,5 @@ root = true [*] end_of_line = lf insert_final_newline = true +indent_size = 4 + diff --git a/MODULE.bazel b/MODULE.bazel index e2f94ab..1a9b5c5 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -7,14 +7,35 @@ 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.2") -bazel_dep(name = "ecsact_runtime", version = "0.6.2") -bazel_dep(name = "ecsact_lang_cpp", version = "0.4.1") +bazel_dep(name = "ecsact_runtime", version = "0.6.6") +bazel_dep(name = "ecsact_lang_cpp", version = "0.4.4") 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.2.0") -bazel_dep(name = "ecsact_cli", version = "0.3.9") +bazel_dep(name = "ecsact_cli", version = "0.3.11") bazel_dep(name = "xxhash", version = "0.8.2") -bazel_dep(name = "googletest", version = "1.14.0.bcr.1") + +# local_path_override( +# module_name = "ecsact_lang_cpp", +# path = "../ecsact_lang_cpp", +# ) + +git_override( + module_name = "ecsact_lang_cpp", + remote = "git@github.com:ecsact-dev/ecsact_lang_cpp.git", + commit = "1add2a6144776659b4f69f3b4132eefb0cb07a50", +) + +# local_path_override( +# module_name = "ecsact_runtime", +# path = "../ecsact_runtime", +# ) + +git_override( + module_name = "ecsact_runtime", + remote = "git@github.com:ecsact-dev/ecsact_runtime.git", + commit = "bb28aff1a1fbf214a0740f94ffddc35bf3f09043", +) bazel_dep(name = "toolchains_llvm", version = "1.0.0", dev_dependency = True) bazel_dep(name = "hedron_compile_commands", dev_dependency = True) @@ -24,13 +45,6 @@ git_override( remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", ) -# TODO: https://github.com/bazelbuild/bazel-central-registry/pull/1916 -git_override( - module_name = "libarchive", - commit = "7c331f92acea5243c195cdc6fb46ecfa11ce1ce2", - remote = "https://github.com/zaucy/libarchive.git", -) - llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm", dev_dependency = True) llvm.toolchain(llvm_version = "17.0.6") use_repo(llvm, "llvm_toolchain") diff --git a/build_recipe.yml b/build_recipe.yml index be98340..6f48e6b 100644 --- a/build_recipe.yml +++ b/build_recipe.yml @@ -57,10 +57,6 @@ sources: outdir: include/entt/entity - fetch: https://raw.githubusercontent.com/skypjack/entt/v3.12.2/src/entt/entity/sparse_set.hpp outdir: include/entt/entity - - fetch: https://raw.githubusercontent.com/skypjack/entt/v3.12.2/src/entt/entity/group.hpp - outdir: include/entt/entity - - fetch: https://raw.githubusercontent.com/skypjack/entt/v3.12.2/src/entt/entity/storage.hpp - outdir: include/entt/entity - fetch: https://raw.githubusercontent.com/skypjack/entt/v3.12.2/src/entt/entity/component.hpp outdir: include/entt/entity - fetch: https://raw.githubusercontent.com/skypjack/entt/v3.12.2/src/entt/entity/mixin.hpp @@ -189,6 +185,12 @@ sources: outdir: include/ecsact/entt/detail - path: ./ecsact/entt/detail/hash.hh outdir: include/ecsact/entt/detail + - path: ./ecsact/entt/detail/assoc_fields_hash.hh + outdir: include/ecsact/entt/detail + - path: ./ecsact/entt/detail/codegen_error.hh + outdir: include/ecsact/entt/detail + - path: ./ecsact/entt/detail/indexed_storage.hh + outdir: include/ecsact/entt/detail # ecsact/entt/wrapper - path: ./ecsact/entt/wrapper/core.hh outdir: include/ecsact/entt/wrapper diff --git a/ecsact/entt/assoc.hh b/ecsact/entt/assoc.hh new file mode 100644 index 0000000..5e3f5ad --- /dev/null +++ b/ecsact/entt/assoc.hh @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace ecsact::entt { + +/** + * This struct is used to tag an entity as 'indexed'. The template parameter @tp + * C is only to make the code generation more clear which component is being + * indexed. Since the storage name is the identifying factor it should have no + * effect. + */ +template +struct indexed {}; + +} // namespace ecsact::entt diff --git a/ecsact/entt/detail/apply_pending.hh b/ecsact/entt/detail/apply_pending.hh index 80162ab..6127385 100644 --- a/ecsact/entt/detail/apply_pending.hh +++ b/ecsact/entt/detail/apply_pending.hh @@ -1,6 +1,7 @@ #pragma once #include +#include "ecsact/entt/detail/registry.hh" #include "ecsact/entt/wrapper/core.hh" #include "ecsact/entt/detail/internal_markers.hh" @@ -11,6 +12,7 @@ auto apply_pending_add(ecsact::entt::registry_t& registry) -> void { if constexpr(std::is_empty_v) { registry.view>().each([&](auto entity) { registry.emplace(entity); + // lifecycle_on_add(registry, entity, comp); }); } else { registry.view>().each( // diff --git a/ecsact/entt/detail/assoc_fields_hash.hh b/ecsact/entt/detail/assoc_fields_hash.hh new file mode 100644 index 0000000..0ec174a --- /dev/null +++ b/ecsact/entt/detail/assoc_fields_hash.hh @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include "entt/entt.hpp" +#include "ecsact/runtime/common.h" + +namespace ecsact::entt::detail { +using assoc_hash_value_t = std::uint32_t; +static_assert( + sizeof(::entt::id_type) == sizeof(assoc_hash_value_t), + "EnTT storage id type must match the size of ecsact_rt_entt internal hash " + "algorithm size" +); + +auto get_assoc_fields_hash( + ecsact_composite_id compo_id, + std::va_list indexed_field_values +) -> assoc_hash_value_t; + +template + requires(C::has_assoc_fields) +auto get_assoc_fields_hash(const C&) -> assoc_hash_value_t; +} // namespace ecsact::entt::detail diff --git a/ecsact/entt/detail/bytes.hh b/ecsact/entt/detail/bytes.hh index 7edc49d..e3d2a60 100644 --- a/ecsact/entt/detail/bytes.hh +++ b/ecsact/entt/detail/bytes.hh @@ -9,7 +9,7 @@ namespace ecsact::entt::detail { template - requires(std::integral || std::floating_point) + requires(std::integral || std::floating_point || std::is_enum_v) constexpr auto bytes_sizeof() -> int { using value_type = std::remove_cvref_t; @@ -43,7 +43,7 @@ template using unsigned_bit_size_equivalent_t = unsigned_bit_size_equivalent::type; template - requires(std::integral || std::floating_point) + requires(std::integral || std::floating_point || std::is_enum_v) auto bytes_copy_into( // T v, auto& out_bytes, diff --git a/ecsact/entt/detail/codegen_error.hh b/ecsact/entt/detail/codegen_error.hh new file mode 100644 index 0000000..87a3f59 --- /dev/null +++ b/ecsact/entt/detail/codegen_error.hh @@ -0,0 +1,7 @@ +#pragma once + +namespace ecsact::entt::detail { +template +constexpr bool unimplemented_by_codegen = false; +} + diff --git a/ecsact/entt/detail/globals.hh b/ecsact/entt/detail/globals.hh index 791da4c..7138ad0 100644 --- a/ecsact/entt/detail/globals.hh +++ b/ecsact/entt/detail/globals.hh @@ -41,6 +41,13 @@ extern std::unordered_map< // */ extern const std::unordered_set all_component_ids; +using add_component_fn_sig_t = ecsact_add_error (*)( + ecsact_registry_id reg_id, + ecsact_entity_id entity_id, + ecsact_component_id component_id, + const void* component_data +); + /** * ecsact_add_component fn pointers * @@ -48,9 +55,16 @@ extern const std::unordered_set all_component_ids; */ extern const std::unordered_map< // ecsact_component_id, - decltype(&ecsact_add_component)> + add_component_fn_sig_t> add_component_fns; +using get_component_fn_sig_t = const void* (*)( // + ecsact_registry_id reg_id, + ecsact_entity_id entity_id, + ecsact_component_id component_id, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash +); + /** * ecsact_get_component fn pointers * @@ -58,9 +72,17 @@ extern const std::unordered_map< // */ extern const std::unordered_map< // ecsact_component_id, - decltype(&ecsact_get_component)> + get_component_fn_sig_t> get_component_fns; +using update_component_fn_sig_t = ecsact_update_error (*)( + ecsact_registry_id reg_id, + ecsact_entity_id entity_id, + ecsact_component_id component_id, + const void* component_data, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash +); + /** * ecsact_update_component fn pointers * @@ -68,9 +90,16 @@ extern const std::unordered_map< // */ extern const std::unordered_map< // ecsact_component_id, - decltype(&ecsact_update_component)> + update_component_fn_sig_t> update_component_fns; +using remove_component_fn_sig_t = void (*)( + ecsact_registry_id reg_id, + ecsact_entity_id entity_id, + ecsact_component_id component_id, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash +); + /** * ecsact_remove_component fn pointers * @@ -78,9 +107,16 @@ extern const std::unordered_map< // */ extern const std::unordered_map< // ecsact_component_id, - decltype(&ecsact_remove_component)> + remove_component_fn_sig_t> remove_component_fns; +using has_component_fn_sig_t = bool (*)( + ecsact_registry_id reg_id, + ecsact_entity_id entity_id, + ecsact_component_id component_id, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash +); + /** * ecsact_has_component fn pointers * @@ -88,79 +124,9 @@ extern const std::unordered_map< // */ extern const std::unordered_map< // ecsact_component_id, - decltype(&ecsact_has_component)> + has_component_fn_sig_t> has_component_fns; -/** - * ecsact_system_execution_context_get fn pointers - * - * NOTE: This gets is filled in by ecsact_rt_entt_codegen - */ -extern const std::unordered_map< // - ecsact_system_like_id, - std::unordered_map< - ecsact::entt::assoc_index, - std::unordered_map< // - ecsact_component_like_id, - decltype(&ecsact_system_execution_context_get)>>> - exec_ctx_get_fns; - -/** - * ecsact_system_execution_context_add fn pointers - * - * NOTE: This adds is filled in by ecsact_rt_entt_codegen - */ -extern const std::unordered_map< // - ecsact_system_like_id, - std::unordered_map< - ecsact::entt::assoc_index, - std::unordered_map< // - ecsact_component_like_id, - decltype(&ecsact_system_execution_context_add)>>> - exec_ctx_add_fns; - -/** - * ecsact_system_execution_context_update fn pointers - * - * NOTE: This updates is filled in by ecsact_rt_entt_codegen - */ -extern const std::unordered_map< // - ecsact_system_like_id, - std::unordered_map< - ecsact::entt::assoc_index, - std::unordered_map< // - ecsact_component_like_id, - decltype(&ecsact_system_execution_context_update)>>> - exec_ctx_update_fns; - -/** - * ecsact_system_execution_context_remove fn pointers - * - * NOTE: This removes is filled in by ecsact_rt_entt_codegen - */ -extern const std::unordered_map< // - ecsact_system_like_id, - std::unordered_map< - ecsact::entt::assoc_index, - std::unordered_map< // - ecsact_component_like_id, - decltype(&ecsact_system_execution_context_remove)>>> - exec_ctx_remove_fns; - -/** - * ecsact_system_execution_context_has fn pointers - * - * NOTE: This has is filled in by ecsact_rt_entt_codegen - */ -extern const std::unordered_map< // - ecsact_system_like_id, - std::unordered_map< - ecsact::entt::assoc_index, - std::unordered_map< // - ecsact_component_like_id, - decltype(&ecsact_system_execution_context_has)>>> - exec_ctx_has_fns; - /** * ecsact_system_execution_context_action fn pointers * diff --git a/ecsact/entt/detail/hash.hh b/ecsact/entt/detail/hash.hh index c5b5392..8b79cba 100644 --- a/ecsact/entt/detail/hash.hh +++ b/ecsact/entt/detail/hash.hh @@ -1,5 +1,6 @@ #pragma once +#include "ecsact/entt/detail/bytes.hh" #include #include @@ -11,8 +12,25 @@ namespace ecsact::entt::detail { * @param data_length length of @p data * @returns hash value */ -auto bytes_hash( // +auto bytes_hash32( // + std::byte* data, + int data_length +) -> std::uint32_t; + +auto bytes_hash64( // std::byte* data, int data_length ) -> std::uint64_t; + +template +auto hash_vals32(Args&&... args) -> std::uint32_t { + auto bytes = bytes_copy(args...); + return bytes_hash32(bytes.data(), bytes.size()); +} + +template +auto hash_vals64(Args&&... args) -> std::uint64_t { + auto bytes = bytes_copy(args...); + return bytes_hash64(bytes.data(), bytes.size()); +} } // namespace ecsact::entt::detail diff --git a/ecsact/entt/detail/indexed_storage.hh b/ecsact/entt/detail/indexed_storage.hh new file mode 100644 index 0000000..bbc27ab --- /dev/null +++ b/ecsact/entt/detail/indexed_storage.hh @@ -0,0 +1,40 @@ +#pragma once + +#include +#include "codegen_error.hh" +#include "ecsact/entt/detail/registry.hh" +#include "ecsact/entt/detail/assoc_fields_hash.hh" +#include "ecsact/entt/detail/codegen_error.hh" + +namespace ecsact::entt::detail { + +template +struct multi_assoc_storage { + std::vector storage_hash_value_ids; + + auto ensure(assoc_hash_value_t id) -> void { + if(std::ranges::find(storage_hash_value_ids, id) == + storage_hash_value_ids.end()) { + storage_hash_value_ids.emplace_back(id); + } + } + + auto erase(assoc_hash_value_t id) -> void { + auto itr = std::ranges::find(storage_hash_value_ids, id); + if(itr != storage_hash_value_ids.end()) { + storage_hash_value_ids.erase(itr); + } + } +}; + +template +auto update_indexed_storage(ecsact::entt::registry_t& registry) -> void { + static_assert(detail::unimplemented_by_codegen, R"( + ----------------------------------------------------------------------------- +| (!) CODEGEN ERROR | +| `update_indexed_storage<>` template specialization cannot be found. This is | +| typically generated by ecsact_rt_entt_codegen. | + ----------------------------------------------------------------------------- +)"); +} +} // namespace ecsact::entt::detail diff --git a/ecsact/entt/detail/system_execution_context.hh b/ecsact/entt/detail/system_execution_context.hh index 16272fb..93b58c1 100644 --- a/ecsact/entt/detail/system_execution_context.hh +++ b/ecsact/entt/detail/system_execution_context.hh @@ -11,6 +11,7 @@ #include "ecsact/entt/event_markers.hh" #include "ecsact/entt/entity.hh" #include "ecsact/entt/detail/registry.hh" +#include "ecsact/entt/detail/assoc_fields_hash.hh" namespace ecsact::entt { /** @@ -55,21 +56,25 @@ struct ecsact_system_execution_context { ) -> void = 0; virtual auto remove( // - ecsact_component_like_id component_id + ecsact_component_like_id component_id, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> void = 0; virtual auto get( // - ecsact_component_like_id component_id, - void* out_component_data + ecsact_component_like_id component_id, + void* out_component_data, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> void = 0; virtual auto update( // - ecsact_component_like_id component_id, - const void* component_data + ecsact_component_like_id component_id, + const void* component_data, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> void = 0; virtual auto has( // - ecsact_component_like_id component_id + ecsact_component_like_id component_id, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> bool = 0; virtual auto generate( // @@ -81,6 +86,6 @@ struct ecsact_system_execution_context { virtual auto parent() -> const ecsact_system_execution_context* = 0; virtual auto other( // - ecsact_entity_id entity + ecsact_system_assoc_id assoc_id ) -> ecsact_system_execution_context* = 0; }; diff --git a/ecsact/entt/entity.hh b/ecsact/entt/entity.hh index b5e80cf..947d6b8 100644 --- a/ecsact/entt/entity.hh +++ b/ecsact/entt/entity.hh @@ -13,6 +13,11 @@ namespace ecsact::entt { class entity_id { std::int32_t _id; + static_assert( + sizeof(::entt::entity) == sizeof(ecsact_entity_id), + "Ecsact entity ID must be same size as EnTT entity ID" + ); + public: inline entity_id() { } diff --git a/ecsact/entt/error_check.hh b/ecsact/entt/error_check.hh index fff545d..9ec764e 100644 --- a/ecsact/entt/error_check.hh +++ b/ecsact/entt/error_check.hh @@ -5,15 +5,11 @@ #include #include #include +#include "detail/codegen_error.hh" #include "ecsact/runtime/common.h" #include "ecsact/runtime/core.h" #include "entt/entity/registry.hpp" -#include "ecsact/entt/entity.hh" - -namespace ecsact::entt::detail { -template -constexpr bool error_check_unimplemented_by_codegen = false; -} +#include "ecsact/entt/detail/codegen_error.hh" namespace ecsact::entt { @@ -23,7 +19,7 @@ auto check_add_component_error( // ::ecsact::entt::entity_id, const C& ) -> ecsact_add_error { - static_assert(detail::error_check_unimplemented_by_codegen, R"( + static_assert(detail::unimplemented_by_codegen, R"( ----------------------------------------------------------------------------- | (!) CODEGEN ERROR | | `check_add_component_error<>` template specialization cannot be found. This | @@ -38,7 +34,7 @@ auto check_update_component_error( // ::ecsact::entt::entity_id, const C& ) -> ecsact_update_error { - static_assert(detail::error_check_unimplemented_by_codegen, R"( + static_assert(detail::unimplemented_by_codegen, R"( ----------------------------------------------------------------------------- | (!) CODEGEN ERROR | | `check_update_component_error<>` template specialization cannot be found. | @@ -52,7 +48,7 @@ auto check_action_error( // ecsact::entt::registry_t&, const A& ) -> ecsact_execute_systems_error { - static_assert(detail::error_check_unimplemented_by_codegen, R"( + static_assert(detail::unimplemented_by_codegen, R"( ----------------------------------------------------------------------------- | (!) CODEGEN ERROR | | `check_action_error<>` template specialization cannot be found. | diff --git a/ecsact/entt/execution.hh b/ecsact/entt/execution.hh index f407d0e..8d25e81 100644 --- a/ecsact/entt/execution.hh +++ b/ecsact/entt/execution.hh @@ -8,11 +8,7 @@ #include "ecsact/entt/entity.hh" #include "entt/entity/registry.hpp" #include "ecsact/entt/detail/globals.hh" - -namespace ecsact::entt::detail { -template -constexpr bool unimplemented_by_codegen = false; -} +#include "ecsact/entt/detail/codegen_error.hh" namespace ecsact::entt { diff --git a/ecsact/entt/wrapper/core.hh b/ecsact/entt/wrapper/core.hh index 901cd1d..70317e4 100644 --- a/ecsact/entt/wrapper/core.hh +++ b/ecsact/entt/wrapper/core.hh @@ -9,33 +9,62 @@ #include "ecsact/entt/registry_util.hh" #include "ecsact/entt/error_check.hh" #include "ecsact/entt/detail/execution_events_collector.hh" +#include "ecsact/entt/detail/assoc_fields_hash.hh" +#include "ecsact/entt/detail/indexed_storage.hh" namespace ecsact::entt::wrapper::core { template inline auto has_component( // - ecsact_registry_id registry_id, - ecsact_entity_id entity_id, - [[maybe_unused]] ecsact_component_id component_id + ecsact_registry_id registry_id, + ecsact_entity_id entity_id, + [[maybe_unused]] ecsact_component_id component_id, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> bool { auto& reg = ecsact::entt::get_registry(registry_id); auto entity = ecsact::entt::entity_id{entity_id}; assert(C::id == component_id); - return reg.all_of(entity); + if constexpr(C::has_assoc_fields) { + auto storage_id = static_cast<::entt::id_type>(assoc_fields_hash); + return reg.storage(storage_id).contains(entity); + } else { + return reg.storage().contains(entity); + } } template inline auto get_component( - ecsact_registry_id registry_id, - ecsact_entity_id entity_id, - [[maybe_unused]] ecsact_component_id component_id + ecsact_registry_id registry_id, + ecsact_entity_id entity_id, + [[maybe_unused]] ecsact_component_id component_id, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> const void* { auto& reg = ecsact::entt::get_registry(registry_id); auto entity = ecsact::entt::entity_id{entity_id}; assert(C::id == component_id); +#ifndef NDEBUG + if(C::id != component_id) { + return nullptr; + } + if(!has_component( + registry_id, + entity_id, + component_id, + assoc_fields_hash + )) { + return nullptr; + } +#endif - const C& comp = reg.get(entity); - return ∁ + if constexpr(C::has_assoc_fields) { + const C& comp = + reg.storage(static_cast<::entt::id_type>(assoc_fields_hash)) + .get(entity); + return ∁ + } else { + const C& comp = reg.get(entity); + return ∁ + } } template @@ -45,6 +74,7 @@ inline auto add_component( // [[maybe_unused]] ecsact_component_id component_id, const void* component_data ) -> ecsact_add_error { + using ecsact::entt::detail::multi_assoc_storage; auto& reg = ecsact::entt::get_registry(registry_id); auto entity = ecsact::entt::entity_id{entity_id}; assert(C::id == component_id); @@ -58,13 +88,22 @@ inline auto add_component( // if(err == ECSACT_ADD_OK) { if constexpr(std::is_empty_v) { reg.emplace(entity); + } else if constexpr(C::has_assoc_fields) { + auto comp = static_cast(component_data); + auto assoc_fields_hash = + ecsact::entt::detail::get_assoc_fields_hash(*comp); + auto storage_id = static_cast<::entt::id_type>(assoc_fields_hash); + reg.storage(storage_id).emplace(entity, *comp); + if(!reg.all_of>(entity)) { + reg.emplace>(entity).ensure(assoc_fields_hash); + } else { + auto& multi_storage = reg.get>(entity); + multi_storage.ensure(assoc_fields_hash); + } } else { - reg.emplace>( - entity, - *static_cast(component_data), - false - ); - reg.emplace(entity, *static_cast(component_data)); + auto comp = static_cast(component_data); + reg.emplace>(entity, *comp, false); + reg.emplace(entity, *comp); } ecsact::entt::detail::add_system_markers_if_needed(reg, entity); @@ -113,10 +152,11 @@ inline auto add_component_exec_options( // template inline auto update_component( // - ecsact_registry_id registry_id, - ecsact_entity_id entity_id, - [[maybe_unused]] ecsact_component_id component_id, - const void* component_data + ecsact_registry_id registry_id, + ecsact_entity_id entity_id, + [[maybe_unused]] ecsact_component_id component_id, + const void* component_data, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> ecsact_update_error { using ecsact::entt::detail::exec_beforechange_storage; @@ -135,20 +175,43 @@ inline auto update_component( // return err; } - const auto& in_component = *static_cast(component_data); - auto& current_component = reg.template get(entity); + auto new_comp = static_cast(component_data); - current_component = in_component; + if constexpr(C::has_assoc_fields) { + auto new_assoc_fields_hash = + ecsact::entt::detail::get_assoc_fields_hash(*new_comp); - return err; + if(new_assoc_fields_hash == assoc_fields_hash) { + auto storage_id = static_cast<::entt::id_type>(assoc_fields_hash); + reg.template storage(storage_id).get(entity) = *new_comp; + } else { + auto new_storage_id = static_cast<::entt::id_type>(new_assoc_fields_hash); + auto old_storage_id = static_cast<::entt::id_type>(assoc_fields_hash); + reg.template storage(old_storage_id).erase(entity); + reg.template storage(new_storage_id).emplace(entity, *new_comp); + } + } else { + reg.template get(entity) = *new_comp; + } + + return ECSACT_UPDATE_OK; } +using update_component_exec_options_sig_t = ecsact_update_error (*)( // + ecsact_registry_id registry_id, + ecsact_entity_id entity_id, + [[maybe_unused]] ecsact_component_id component_id, + const void* component_data, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash +); + template inline auto update_component_exec_options( // - ecsact_registry_id registry_id, - ecsact_entity_id entity_id, - [[maybe_unused]] ecsact_component_id component_id, - const void* component_data + ecsact_registry_id registry_id, + ecsact_entity_id entity_id, + [[maybe_unused]] ecsact_component_id component_id, + const void* component_data, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> ecsact_update_error { using ecsact::entt::detail::exec_beforechange_storage; @@ -180,30 +243,48 @@ inline auto update_component_exec_options( // return err; } +static_assert(std::is_same_v< + update_component_exec_options_sig_t, + decltype(&update_component_exec_options)>); + template auto remove_component( - ecsact_registry_id registry_id, - ecsact_entity_id entity_id, - [[maybe_unused]] ecsact_component_id component_id + ecsact_registry_id registry_id, + ecsact_entity_id entity_id, + [[maybe_unused]] ecsact_component_id component_id, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> void { auto& reg = ecsact::entt::get_registry(registry_id); auto entity = ecsact::entt::entity_id{entity_id}; assert(C::id == component_id); - reg.remove(entity); - if constexpr(!std::is_empty_v) { - reg.remove>(entity); + if constexpr(C::has_assoc_fields) { + auto storage_id = static_cast<::entt::id_type>(assoc_fields_hash); + reg.storage(storage_id).erase(entity); + } else { + reg.remove(entity); + if constexpr(!std::is_empty_v) { + reg.remove>(entity); + } + reg.template remove>(entity); + reg.template emplace_or_replace>(entity); + ecsact::entt::detail::remove_system_markers_if_needed(reg, entity); } - reg.template remove>(entity); - reg.template emplace_or_replace>(entity); - ecsact::entt::detail::remove_system_markers_if_needed(reg, entity); } +using remove_component_exec_options_sig_t = void (*)( // + ecsact_registry_id registry_id, + ecsact_entity_id entity_id, + [[maybe_unused]] ecsact_component_id component_id, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash +); + template auto remove_component_exec_options( - ecsact_registry_id registry_id, - ecsact_entity_id entity_id, - [[maybe_unused]] ecsact_component_id component_id + ecsact_registry_id registry_id, + ecsact_entity_id entity_id, + [[maybe_unused]] ecsact_component_id component_id, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> void { using ecsact::entt::detail::pending_remove; @@ -229,6 +310,10 @@ auto remove_component_exec_options( ecsact::entt::detail::remove_system_markers_if_needed(reg, entity); } +static_assert(std::is_same_v< + remove_component_exec_options_sig_t, + decltype(&remove_component_exec_options)>); + inline auto _trigger_create_entity_events( ecsact_registry_id registry_id, ecsact::entt::detail::execution_events_collector& events_collector diff --git a/ecsact/entt/wrapper/dynamic.hh b/ecsact/entt/wrapper/dynamic.hh index 7007384..159bccc 100644 --- a/ecsact/entt/wrapper/dynamic.hh +++ b/ecsact/entt/wrapper/dynamic.hh @@ -9,6 +9,7 @@ #include "ecsact/entt/detail/internal_markers.hh" #include "ecsact/entt/event_markers.hh" #include "ecsact/entt/detail/system_execution_context.hh" +#include "ecsact/entt/detail/assoc_fields_hash.hh" namespace ecsact::entt::wrapper::dynamic { @@ -22,6 +23,7 @@ auto context_add( using ecsact::entt::component_removed; using ecsact::entt::detail::beforeremove_storage; using ecsact::entt::detail::exec_beforechange_storage; + using ecsact::entt::detail::get_assoc_fields_hash; using ecsact::entt::detail::pending_add; assert(ecsact_id_cast(C::id) == component_id); @@ -31,6 +33,11 @@ auto context_add( if constexpr(std::is_empty_v) { registry.template emplace_or_replace>(entity); + } else if constexpr(C::has_assoc_fields) { + auto component = static_cast(component_data); + auto assoc_fields_hash = get_assoc_fields_hash(*component); + auto storage_id = static_cast<::entt::id_type>(assoc_fields_hash); + registry.template storage(storage_id).emplace(entity, *component); } else { const C* component = static_cast(component_data); registry.template emplace_or_replace>(entity, *component); @@ -70,7 +77,8 @@ template auto context_remove( ecsact_system_execution_context* context, [[maybe_unused]] ecsact_component_like_id component_id, - auto& view + auto& view, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> void { assert(ecsact_id_cast(C::id) == component_id); @@ -125,10 +133,10 @@ auto context_get( ecsact_system_execution_context* context, [[maybe_unused]] ecsact_component_like_id component_id, void* out_component_data, - auto& view + auto& view, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> void { auto entity = context->entity; - *static_cast(out_component_data) = view.template get(entity); } @@ -137,7 +145,8 @@ auto context_update( ecsact_system_execution_context* context, [[maybe_unused]] ecsact_component_like_id component_id, const void* in_component_data, - auto& view + auto& view, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> void { using ecsact::entt::detail::exec_beforechange_storage; // TODO(Kelwan): for remove, beforeremove_storage @@ -158,7 +167,8 @@ auto context_update( template auto context_has( ecsact_system_execution_context* context, - [[maybe_unused]] ecsact_component_like_id component_id + [[maybe_unused]] ecsact_component_like_id component_id, + ecsact::entt::detail::assoc_hash_value_t assoc_fields_hash ) -> bool { auto entity = context->entity; auto& registry = *context->registry; diff --git a/rt_entt_codegen/core/BUILD.bazel b/rt_entt_codegen/core/BUILD.bazel index cd4b655..c9c4b87 100644 --- a/rt_entt_codegen/core/BUILD.bazel +++ b/rt_entt_codegen/core/BUILD.bazel @@ -11,6 +11,8 @@ cc_library( # keep sorted _CORE_CODEGEN_METHODS = { + "assoc_fields_hash": [], + "update_indexed_storage": [], "execute_systems": [ "//rt_entt_codegen/shared:parallel", "//rt_entt_codegen/shared:system_variant", @@ -33,7 +35,6 @@ _CORE_CODEGEN_METHODS = { "//rt_entt_codegen/core/system_provider:basic", "//rt_entt_codegen/core/system_provider:parallel", "//rt_entt_codegen/core/system_provider", - "@entt//:entt", "@ecsact_rt_entt//:lib", ], "check_error": [], @@ -47,6 +48,7 @@ _CORE_CODEGEN_METHODS = { ], "system_notify": ["//rt_entt_codegen/shared:system_util"], "update_beforechange": [], + "assoc_globals": [], } [cc_library( diff --git a/rt_entt_codegen/core/assoc_fields_hash.cc b/rt_entt_codegen/core/assoc_fields_hash.cc new file mode 100644 index 0000000..7168a50 --- /dev/null +++ b/rt_entt_codegen/core/assoc_fields_hash.cc @@ -0,0 +1,144 @@ + +#include "rt_entt_codegen/core/core.hh" + +#include +#include +#include "ecsact/runtime/meta.hh" +#include "ecsact/lang-support/lang-cc.hh" +#include "ecsact/cpp_codegen_plugin_util.hh" + +using ecsact::codegen_plugin_context; +using ecsact::cc_lang_support::c_identifier; +using ecsact::cc_lang_support::cpp_field_type_name; +using ecsact::cc_lang_support::cpp_identifier; +using ecsact::cpp_codegen_plugin_util::block; +using ecsact::cpp_codegen_plugin_util::comma_delim; +using ecsact::cpp_codegen_plugin_util::method_printer; +using ecsact::meta::decl_full_name; +using ecsact::meta::system_assoc_ids; +using ecsact::rt_entt_codegen::ecsact_entt_details; + +template +static auto get_assoc_fields(CompositeID compo_id +) -> std::vector { + auto result = std::vector{}; + + for(auto field_id : ecsact::meta::get_field_ids(compo_id)) { + auto field_type = ecsact::meta::get_field_type(compo_id, field_id); + if(field_type.kind == ECSACT_TYPE_KIND_BUILTIN && + field_type.type.builtin == ECSACT_ENTITY_TYPE) { + result.push_back(field_id); + } + + if(field_type.kind == ECSACT_TYPE_KIND_FIELD_INDEX) { + result.push_back(field_id); + } + } + + return result; +} + +auto print_assoc_fields_hash_va_list( + codegen_plugin_context& ctx, + const ecsact_entt_details& details +) -> void { + auto printer = // + method_printer{ctx, "ecsact::entt::detail::get_assoc_fields_hash"} // + .parameter("ecsact_composite_id", "compo_id") + .parameter("std::va_list", "indexed_field_values") + .return_type("ecsact::entt::detail::assoc_hash_value_t"); + + for(auto comp_id : details.all_components) { + auto compo_cpp_identifier = cpp_identifier(decl_full_name(comp_id)); + auto assoc_fields = get_assoc_fields(comp_id); + if(assoc_fields.empty()) { + continue; + } + + block( + ctx, + std::format( + "if(ecsact_id_cast({}::id) == compo_id)", + compo_cpp_identifier + ), + [&] { + for(auto field_id : assoc_fields) { + auto field_name = ecsact::meta::field_name(comp_id, field_id); + auto field_type = ecsact::meta::get_field_type(comp_id, field_id); + auto field_cpp_type = cpp_field_type_name(field_type); + ctx.write(std::format( + "auto {} = va_arg(indexed_field_values, {});\n", + field_name, + field_cpp_type + )); + } + + ctx.write(std::format( + "return " + "::ecsact::entt::detail::hash_vals32(static_cast({}::id), " + "{});", + compo_cpp_identifier, + comma_delim(std::views::transform( + assoc_fields, + [&](auto field_id) -> std::string { + return ecsact::meta::field_name(comp_id, field_id); + } + )) + )); + } + ); + ctx.write("\n"); + } + + ctx.write("return 0;"); +} + +auto print_assoc_fields_hash_spec( + codegen_plugin_context& ctx, + const ecsact_entt_details& details, + ecsact_component_id comp_id +) -> void { + auto assoc_fields = get_assoc_fields(comp_id); + if(assoc_fields.empty()) { + return; + } + + auto comp_cpp_ident = cpp_identifier(decl_full_name(comp_id)); + ctx.write("template<> "); + auto printer = // + method_printer{ + ctx, + std::format( + "ecsact::entt::detail::get_assoc_fields_hash<{}>", + comp_cpp_ident + ) + } // + .parameter(std::format("const {}&", comp_cpp_ident), "comp") + .return_type("ecsact::entt::detail::assoc_hash_value_t"); + + ctx.write(std::format( + "return " + "::ecsact::entt::detail::hash_vals32(static_cast({}::id), " + "{});", + comp_cpp_ident, + comma_delim(std::views::transform( + assoc_fields, + [&](auto field_id) -> std::string { + return std::format( + "comp.{}", + ecsact::meta::field_name(comp_id, field_id) + ); + } + )) + )); +} + +auto ecsact::rt_entt_codegen::core::print_assoc_fields_hash( + codegen_plugin_context& ctx, + const ecsact_entt_details& details +) -> void { + print_assoc_fields_hash_va_list(ctx, details); + for(auto comp_id : details.all_components) { + print_assoc_fields_hash_spec(ctx, details, comp_id); + } +} diff --git a/rt_entt_codegen/core/assoc_globals.cc b/rt_entt_codegen/core/assoc_globals.cc new file mode 100644 index 0000000..97db9e3 --- /dev/null +++ b/rt_entt_codegen/core/assoc_globals.cc @@ -0,0 +1,29 @@ +#include "rt_entt_codegen/core/core.hh" + +#include +#include "ecsact/runtime/meta.hh" +#include "ecsact/lang-support/lang-cc.hh" + +using ecsact::cc_lang_support::c_identifier; +using ecsact::meta::decl_full_name; +using ecsact::meta::system_assoc_ids; + +auto ecsact::rt_entt_codegen::core::print_assoc_globals( + codegen_plugin_context& ctx, + const ecsact_entt_details& details +) -> void { + for(auto sys_id : details.all_systems) { + auto assoc_ids = system_assoc_ids(sys_id); + for(auto i = 0; assoc_ids.size() > i; ++i) { + auto assoc_id = assoc_ids.at(i); + auto assoc_global_name = + std::format("{}__{}", c_identifier(decl_full_name(sys_id)), i); + ctx.write(std::format( + "extern \"C\" ecsact_system_assoc_id {} = " + "static_cast({});\n", + assoc_global_name, + i + )); + } + } +} diff --git a/rt_entt_codegen/core/core.hh b/rt_entt_codegen/core/core.hh index 532d98d..716a93a 100644 --- a/rt_entt_codegen/core/core.hh +++ b/rt_entt_codegen/core/core.hh @@ -6,6 +6,21 @@ namespace ecsact::rt_entt_codegen::core { +auto print_update_indexed_storage( + codegen_plugin_context& ctx, + const ecsact_entt_details& details +) -> void; + +auto print_assoc_fields_hash( + codegen_plugin_context& ctx, + const ecsact_entt_details& details +) -> void; + +auto print_assoc_globals( + codegen_plugin_context& ctx, + const ecsact_entt_details& details +) -> void; + auto print_parallel_system_execute( codegen_plugin_context& ctx, const ecsact_entt_details& details diff --git a/rt_entt_codegen/core/execution_options.cc b/rt_entt_codegen/core/execution_options.cc index 636e02d..3069c64 100644 --- a/rt_entt_codegen/core/execution_options.cc +++ b/rt_entt_codegen/core/execution_options.cc @@ -39,7 +39,7 @@ inline auto print_static_maps( ctx, "static const auto execution_update_fns=" "std::unordered_map\n", + "ecsact::entt::wrapper::core::update_component_exec_options_sig_t>\n", [&] { for(auto component_id : details.all_components) { auto type_name = cpp_identifier(decl_full_name(component_id)); @@ -68,7 +68,7 @@ inline auto print_static_maps( ctx, "static const auto execution_remove_fns= " "std::unordered_map\n", + "ecsact::entt::wrapper::core::remove_component_exec_options_sig_t>\n", [&] { for(auto component_id : details.all_components) { auto type_name = cpp_identifier(decl_full_name(component_id)); @@ -179,25 +179,33 @@ auto ecsact::rt_entt_codegen::core::print_execution_options( block(ctx, "for(int i = 0; i < options.update_components_length; i++)", [&] { ctx.write("auto& component = options.update_components[i];\n"); - ctx.write("auto entity = options.update_components_entities[i];\n\n"); + ctx.write("auto entity = options.update_components_entities[i];\n"); + ctx.write( + "auto assoc_fields_hash = ecsact::entt::detail::assoc_hash_value_t{}; // " + "TODO\n\n" + ); ctx.write( "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, assoc_fields_hash);\n" ); }); block(ctx, "for(int i = 0; i < options.remove_components_length; i++)", [&] { ctx.write("auto& component_id = options.remove_components[i];\n"); ctx.write("auto entity = options.remove_components_entities[i];\n\n"); + ctx.write( + "auto assoc_fields_hash = ecsact::entt::detail::assoc_hash_value_t{}; // " + "TODO\n\n" + ); ctx.write( "execution_remove_fns.at(ecsact_id_cast(" "component_id))(registry_id, " "ecsact::entt::entity_id(entity), " - "component_id);\n\n" + "component_id, assoc_fields_hash);\n\n" ); }); diff --git a/rt_entt_codegen/core/print_sys_exec.cc b/rt_entt_codegen/core/print_sys_exec.cc index c81f1a3..a1249b4 100644 --- a/rt_entt_codegen/core/print_sys_exec.cc +++ b/rt_entt_codegen/core/print_sys_exec.cc @@ -32,17 +32,24 @@ template concept system_or_action = std::same_as || std::same_as; +using ecsact::cc_lang_support::c_identifier; using ecsact::cc_lang_support::cpp_identifier; using ecsact::cpp_codegen_plugin_util::block; +using ecsact::cpp_codegen_plugin_util::block_printer; +using ecsact::cpp_codegen_plugin_util::comma_delim; using ecsact::meta::decl_full_name; using ecsact::rt_entt_codegen::ecsact_entt_system_details; using ecsact::rt_entt_codegen::system_comps_with_caps; using ecsact::rt_entt_codegen::system_like_id_variant; +using ecsact::rt_entt_codegen::core::provider::context_view_storage_struct_impl; using ecsact::rt_entt_codegen::core::provider::handle_exclusive_provide; using ecsact::rt_entt_codegen::core::provider::system_provider; using ecsact::rt_entt_codegen::system_util::is_trivial_system; +using ecsact::rt_entt_codegen::util::make_view; +using ecsact::rt_entt_codegen::util::make_view_options; using ecsact::rt_entt_codegen::util::method_printer; using namespace ecsact::rt_entt_codegen::core; +using namespace std::string_literals; using system_provider_t = std::vector>; @@ -95,6 +102,10 @@ static auto print_sys_exec_ctx_remove( auto printer = // method_printer{ctx, "remove"} .parameter("ecsact_component_like_id", "component_id") + .parameter( + "ecsact::entt::detail::assoc_hash_value_t", + "assoc_fields_hash" + ) .return_type("void final"); auto result = std::ranges::find_if(system_providers, [&](auto provider) { @@ -116,6 +127,10 @@ static auto print_sys_exec_ctx_get( method_printer{ctx, "get"} .parameter("ecsact_component_like_id", "component_id") .parameter("void*", "out_component_data") + .parameter( + "ecsact::entt::detail::assoc_hash_value_t", + "assoc_fields_hash" + ) .return_type("void final"); auto result = std::ranges::find_if(system_providers, [&](auto provider) { @@ -137,6 +152,10 @@ static auto print_sys_exec_ctx_update( method_printer{ctx, "update"} .parameter("ecsact_component_like_id", "component_id") .parameter("const void*", "component_data") + .parameter( + "ecsact::entt::detail::assoc_hash_value_t", + "assoc_fields_hash" + ) .return_type("void final"); auto result = std::ranges::find_if(system_providers, [&](auto provider) { @@ -157,6 +176,10 @@ static auto print_sys_exec_ctx_has( auto printer = // method_printer{ctx, "has"} .parameter("ecsact_component_like_id", "component_id") + .parameter( + "ecsact::entt::detail::assoc_hash_value_t", + "assoc_fields_hash" + ) .return_type("bool final"); auto result = std::ranges::find_if(system_providers, [&](auto provider) { @@ -218,7 +241,7 @@ auto print_sys_exec_ctx_other( ) -> void { auto printer = // method_printer{ctx, "other"} - .parameter("ecsact_entity_id", "entity") + .parameter("ecsact_system_assoc_id", "assoc_id") .return_type("ecsact_system_execution_context* final"); auto result = std::ranges::find_if(system_providers, [&](auto provider) { @@ -289,18 +312,18 @@ static auto print_system_execution_context( system_provider_t system_providers ) -> std::string { auto system_name = cpp_identifier(decl_full_name(sys_like_id)); - auto context_type_name = - ecsact::rt_entt_codegen::system_util::create_context_struct_name(sys_like_id - ); - + std::format("{}__Context", c_identifier(decl_full_name(sys_like_id))); auto struct_header = std::format( "struct {}: ecsact_system_execution_context ", context_type_name ); + auto system_capabilities = + ecsact::meta::system_capabilities_list(sys_like_id); block(ctx, struct_header, [&] { ctx.write("view_t* view;\n"); + context_view_storage_struct_impl(ctx, system_capabilities); for(const auto& provider : system_providers) { provider->context_function_header(ctx, names); @@ -370,32 +393,70 @@ static auto setup_system_providers(system_like_id_variant sys_like_id return system_providers; } +static auto get_assoc_comps( // + system_like_id_variant sys_like_id +) -> std::vector { + auto assoc_comps = std::vector{}; + for(auto assoc_id : ecsact::meta::system_assoc_ids(sys_like_id)) { + auto assoc_comp_id = + ecsact::meta::system_assoc_component_id(sys_like_id, assoc_id); + if(std::ranges::find(assoc_comps, assoc_comp_id) == assoc_comps.end()) { + assoc_comps.push_back(assoc_comp_id); + } + } + + return assoc_comps; +} + static auto print_execute_systems( ecsact::codegen_plugin_context& ctx, system_like_id_variant sys_like_id, const common_vars names ) -> void { + auto assoc_comps = get_assoc_comps(sys_like_id); + auto assoc_view_block = std::optional{}; + + // if(!assoc_comps.empty()) { + // ctx.write(std::format( + // "auto assoc_multi_storage_view = {}.view<{}>();\n", + // names.registry_var_name, + // comma_delim( + // assoc_comps | std::views::transform([](auto comp_id) -> std::string { + // auto comp_cpp_ident = cpp_identifier(decl_full_name(comp_id)); + // return std::format( + // "ecsact::entt::detail::multi_assoc_storage<{}>", + // comp_cpp_ident + // ); + // }) + // ) + // )); + // + // ctx.write("for(auto assoc_multi_entity : assoc_multi_storage_view)"); + // assoc_view_block.emplace(ctx); + // } + auto sys_caps = ecsact::meta::system_capabilities(sys_like_id); auto system_providers = setup_system_providers(sys_like_id); + auto sys_details = ecsact_entt_system_details::from_system_like(sys_like_id); + auto make_view_opts = make_view_options(sys_details); + make_view_opts.registry_var_name = names.registry_var_name; + make_view_opts.sys_like_id = sys_like_id; + make_view_opts.without_multi_component_storage = false; + make_view_opts.view_var_name = "view"; for(const auto& provider : system_providers) { provider->initialization(ctx, names); } - auto additional_view_components = std::vector{}; for(const auto& provider : system_providers) { - provider->before_make_view_or_group(ctx, names, additional_view_components); + provider->before_make_view_or_group( + ctx, + names, + make_view_opts.additional_components + ); } - auto sys_details = ecsact_entt_system_details::from_system_like(sys_like_id); - - ecsact::rt_entt_codegen::util::make_view( - ctx, - "view", - names.registry_var_name, - sys_details, - additional_view_components - ); + make_view(ctx, make_view_opts); ctx.write("using view_t = decltype(view);\n"); @@ -454,6 +515,19 @@ static auto print_execute_systems( } } + ctx.write( + "// TODO: Optimize this by only going over components that can have " + "indexed storage\n" + ); + for(auto comp_id : sys_details.get_all_writable_comps()) { + auto comp_cpp_ident = cpp_identifier(decl_full_name(comp_id)); + ctx.write(std::format( + "::ecsact::entt::detail::update_indexed_storage<{}>({});\n", + comp_cpp_ident, + names.registry_var_name + )); + } + for(const auto& provider : system_providers) { provider->post_iteration(ctx, names); } @@ -542,6 +616,7 @@ static auto print_execute_system_template_specialization( ); block(ctx, "if(system_impl == nullptr)", [&] { ctx.write("return;"); }); + ctx.write("\n"); print_execute_systems( ctx, @@ -592,6 +667,7 @@ static auto print_execute_actions_template_specialization( ); block(ctx, "if(system_impl == nullptr)", [&] { ctx.write("return;"); }); + ctx.write("\n"); ctx.write( "auto actions = actions_map.as_action_span<", diff --git a/rt_entt_codegen/core/sorting_components.cc b/rt_entt_codegen/core/sorting_components.cc index 3cba878..4c5f417 100644 --- a/rt_entt_codegen/core/sorting_components.cc +++ b/rt_entt_codegen/core/sorting_components.cc @@ -101,7 +101,7 @@ static auto print_system_entity_sorting_component_struct( ctx.write("\n);\n"); ctx.write( - "sorted.hash = ::ecsact::entt::detail::bytes_hash(bytes.data(), " + "sorted.hash = ::ecsact::entt::detail::bytes_hash64(bytes.data(), " "bytes.size());" ); }); diff --git a/rt_entt_codegen/core/system_provider/association/association.cc b/rt_entt_codegen/core/system_provider/association/association.cc index 2a99ff7..b3a9428 100644 --- a/rt_entt_codegen/core/system_provider/association/association.cc +++ b/rt_entt_codegen/core/system_provider/association/association.cc @@ -1,5 +1,6 @@ #include "association.hh" +#include #include #include "ecsact/lang-support/lang-cc.hh" @@ -8,176 +9,313 @@ #include "ecsact/runtime/meta.hh" #include "ecsact/cpp_codegen_plugin_util.hh" +using ecsact::cc_lang_support::cpp_identifier; +using ecsact::cpp_codegen_plugin_util::block; +using ecsact::meta::decl_full_name; +using ecsact::meta::system_assoc_ids; +using ecsact::rt_entt_codegen::ecsact_entt_system_details; +using ecsact::rt_entt_codegen::system_util::get_assoc_context_type_name; +using ecsact::rt_entt_codegen::system_util::get_assoc_context_var_name; +using ecsact::rt_entt_codegen::system_util::get_unique_view_name; +using ecsact::rt_entt_codegen::util::method_printer; + using capability_t = std::unordered_map; using namespace ecsact::rt_entt_codegen::core; +auto provider::association::initialization( + codegen_plugin_context& ctx, + const common_vars& names +) -> void { + auto assoc_ids = ecsact::meta::system_assoc_ids(sys_like_id); + for(auto assoc_id : assoc_ids) { + auto assoc_comp_id = + ecsact::meta::system_assoc_component_id(sys_like_id, assoc_id); + auto assoc_field_ids = + ecsact::meta::system_assoc_fields(sys_like_id, assoc_id); + assoc_view_names.insert({assoc_id, get_unique_view_name()}); + + for(auto field_id : assoc_field_ids) { + auto field_type = ecsact::meta::get_field_type(assoc_comp_id, field_id); + if(field_type.kind == ECSACT_TYPE_KIND_BUILTIN && + field_type.type.builtin == ECSACT_ENTITY_TYPE) { + auto compo_id = ecsact_id_cast(assoc_comp_id); + assoc_fields[compo_id].push_back(field_id); + assoc_composites[assoc_id].insert(compo_id); + } else if(field_type.kind == ECSACT_TYPE_KIND_FIELD_INDEX) { + auto compo_id = field_type.type.field_index.composite_id; + assoc_fields[compo_id].push_back(field_id); + assoc_composites[assoc_id].insert(compo_id); + } else { + // Should never get here. Association fields may only be an indexed + // field or entity field. + assert(false); + } + } + } +} + auto provider::association::context_function_header( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::core::common_vars& names + codegen_plugin_context& ctx, + const common_vars& names ) -> void { - ctx.write( - "std::unordered_map " - "other_contexts;\n\n" - ); + auto assoc_ids = system_assoc_ids(sys_like_id); + + ctx.write("// Guaranteed order: "); + ctx.write(cpp_codegen_plugin_util::comma_delim( + assoc_ids | std::views::transform([&](auto assoc_id) -> std::string { + return get_assoc_context_type_name(sys_like_id, assoc_id); + }) + )); + ctx.write("\n"); + ctx.write(std::format( + "std::array other_contexts;\n\n", + assoc_ids.size() + )); +} + +static auto push_back_unique(auto& vec, const auto& element) -> void { + if(std::ranges::find(vec, element) == std::end(vec)) { + vec.push_back(element); + } +} + +auto provider::association::before_make_view_or_group( + codegen_plugin_context& ctx, + const common_vars& names, + std::vector& additional_view_components +) -> void { + // for(auto assoc_id : ecsact::meta::system_assoc_ids(sys_like_id)) { + // auto assoc_caps = + // ecsact::meta::system_assoc_capabilities(sys_like_id, assoc_id); + // auto assoc_system_details = + // ecsact_entt_system_details::from_capabilities(assoc_caps); + // + // for(auto compo_id : assoc_composites.at(assoc_id)) { + // // TODO: At the time of writing this is safe. It's very possible we + // // allow actions to be referenecd in association fields in the near + // // future and at that point this must be addressed. + // auto comp_like_id = static_cast(compo_id); + // if(!assoc_system_details.get_comps.contains(comp_like_id)) { + // auto comp_cpp_ident = cpp_identifier(decl_full_name(comp_like_id)); + // push_back_unique(additional_view_components, comp_cpp_ident); + // } + // } + // } } auto provider::association::after_make_view_or_group( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::core::common_vars& names + codegen_plugin_context& ctx, + const common_vars& names ) -> void { } auto provider::association::context_function_other( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::core::common_vars& names + codegen_plugin_context& ctx, + const common_vars& names ) -> handle_exclusive_provide { context_other_impl(ctx, sys_like_id, system_details); return HANDLED; } -auto provider::association::pre_entity_iteration( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::core::common_vars& names -) -> void { - print_other_contexts(ctx, names); -} - -auto provider::association::pre_exec_system_impl( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::core::common_vars& names -) -> void { - using ecsact::cc_lang_support::cpp_identifier; - using ecsact::meta::decl_full_name; - using ecsact::rt_entt_codegen::system_util::create_context_var_name; +template +static auto get_assoc_fields(CompositeID compo_id +) -> std::vector { + auto result = std::vector{}; - int comp_iter = 0; + for(auto field_id : ecsact::meta::get_field_ids(compo_id)) { + auto field_type = ecsact::meta::get_field_type(compo_id, field_id); + if(field_type.kind == ECSACT_TYPE_KIND_BUILTIN && + field_type.type.builtin == ECSACT_ENTITY_TYPE) { + result.push_back(field_id); + } - for(auto [ids, view_name] : other_view_names) { - if(!components_with_entity_fields.contains(ids.component_like_id)) { - components_with_entity_fields[ids.component_like_id] = - "assoc_comp_" + std::to_string(comp_iter++); + if(field_type.kind == ECSACT_TYPE_KIND_FIELD_INDEX) { + result.push_back(field_id); } } - for(auto&& [comp_like_id, comp_var] : components_with_entity_fields) { - auto comp_name = cpp_identifier(decl_full_name(comp_like_id)); - ctx.write("auto ", comp_var, " = view.get<", comp_name, ">(entity);\n"); + return result; +} + +static auto has_assoc_fields(ecsact::rt_entt_codegen::system_like_id_variant id +) -> bool { + for(auto&& [comp_id, _] : ecsact::meta::system_capabilities_list(id)) { + if(!get_assoc_fields(comp_id).empty()) { + return true; + } } + return false; +} - ctx.write("auto found_assoc_entities = 0;\n"); - - for(auto [ids, view_name] : other_view_names) { - auto field_name = ecsact_meta_field_name( - ecsact_id_cast(ids.component_like_id), - ids.field_id - ); - - auto entity_field_access = - components_with_entity_fields.at(ids.component_like_id) + "." + - field_name; - - auto view_itr_name = view_name + "_itr"; - ctx.write( - "auto ", - view_itr_name, - " = ", - view_name, - ".find(ecsact::entt::entity_id{", - entity_field_access, - "});\n" - ); - - ctx.write( - "if(", - view_itr_name, - " == ", - view_name, - ".end()) { continue; }\n" - ); - - ctx.write("found_assoc_entities += 1;\n"); - - auto context_name = create_context_var_name(ids.component_like_id); - ctx.write(context_name, ".entity = ", entity_field_access, ";\n"); - - ctx.write( - "context.other_contexts.insert(context.other_contexts.end(), " - "std::pair(", - context_name, - ".entity, &", - context_name, - "));\n" - ); +static auto get_first_assoc_comp( + ecsact::rt_entt_codegen::system_like_id_variant id +) -> std::optional { + for(auto&& [comp_id, _] : ecsact::meta::system_capabilities_list(id)) { + if(!get_assoc_fields(comp_id).empty()) { + return comp_id; + } } + return {}; } -auto provider::association::system_impl( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::core::common_vars& names +auto provider::association::entity_iteration( + codegen_plugin_context& ctx, + const common_vars& names, + std::function iter_func ) -> handle_exclusive_provide { - using ecsact::cpp_codegen_plugin_util::block; - - // we need to check if we found any invalid associations - block( - ctx, - "if(found_assoc_entities == " + std::to_string(other_view_names.size()) + - ")", - [&] { ctx.write("system_impl(&context);\n"); } - ); + auto assoc_ids = ecsact::meta::system_assoc_ids(sys_like_id); + + auto assoc_multi_view_block = + std::optional{}; + block(ctx, "for(auto entity : view)", [&] { + if(has_assoc_fields(sys_like_id)) { + ctx.write(std::format( // + "for(auto storage_id : " + "view.get>(entity)." + "storage_hash_value_ids)", + cpp_identifier(decl_full_name(get_first_assoc_comp(sys_like_id).value()) + ) + )); + assoc_multi_view_block.emplace(ctx); + ctx.write(std::format( + "context.storage.c{} = ®istry.storage<{}>(storage_id);", + static_cast(get_first_assoc_comp(sys_like_id).value()), + cpp_identifier(decl_full_name(get_first_assoc_comp(sys_like_id).value()) + ) + )); + } + + for(auto assoc_id : assoc_ids) { + auto assoc_comp = + ecsact::meta::system_assoc_component_id(sys_like_id, assoc_id); + auto assoc_comp_cpp_ident = cpp_identifier(decl_full_name(assoc_comp)); + auto assoc_caps = + ecsact::meta::system_assoc_capabilities(sys_like_id, assoc_id); + auto assoc_system_details = + ecsact_entt_system_details::from_capabilities(assoc_caps); + auto make_view_opts = util::make_view_options(assoc_system_details); + make_view_opts.view_var_name = assoc_view_names.at(assoc_id); + make_view_opts.registry_var_name = names.registry_var_name; + + push_back_unique( + make_view_opts.additional_components, + assoc_comp_cpp_ident + ); + + util::make_view(ctx, make_view_opts); + } + + print_other_contexts(ctx, names); + + for(auto assoc_id : ecsact::meta::system_assoc_ids(sys_like_id)) { + auto assoc_comp_id = + ecsact::meta::system_assoc_component_id(sys_like_id, assoc_id); + auto assoc_comp_cpp_ident = cpp_identifier(decl_full_name(assoc_comp_id)); + auto assoc_comp_field_ids = + ecsact::meta::system_assoc_fields(sys_like_id, assoc_id); + + ctx.write(std::format( + "{0}.storage({1}.storage<{2}>(static_cast<::entt::id_type>(" + "::ecsact::entt::detail::hash_vals32({2}::id, {3})" + ")));\n", + assoc_view_names.at(assoc_id), + names.registry_var_name, + assoc_comp_cpp_ident, + util::comma_delim( + assoc_comp_field_ids | + std::views::transform([&](auto field_id) -> std::string { + return std::format( + "context.storage.c{}->get(entity).{}", + static_cast(assoc_comp_id), + ecsact::meta::field_name(assoc_comp_id, field_id) + ); + }) + ) + )); + } + + for(auto assoc_index = 0; assoc_ids.size() > assoc_index; ++assoc_index) { + auto assoc_id = assoc_ids.at(assoc_index); + ctx.write(std::format( + "auto {0}_itr = {0}.begin();\n", + assoc_view_names.at(assoc_id) + )); + + ctx.write(std::format( + "context.other_contexts[{}] = &{};\n", + assoc_index, + get_assoc_context_var_name(sys_like_id, assoc_id) + )); + } + + block(ctx, "for(;;)", [&] { + for(auto assoc_id : ecsact::meta::system_assoc_ids(sys_like_id)) { + ctx.write(std::format( + "if({0}_itr == {0}.end()) break;\n", + assoc_view_names.at(assoc_id) + )); + } + + for(auto assoc_id : ecsact::meta::system_assoc_ids(sys_like_id)) { + ctx.write(std::format( + "{}.entity = *{}_itr;\n", + get_assoc_context_var_name(sys_like_id, assoc_id), + assoc_view_names.at(assoc_id) + )); + } + + iter_func(); + + for(auto assoc_id : ecsact::meta::system_assoc_ids(sys_like_id)) { + ctx.write(std::format("{}_itr++;\n", assoc_view_names.at(assoc_id))); + } + }); + }); + return HANDLED; } -auto provider::association::print_other_contexts( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::core::common_vars& names +auto provider::association::pre_entity_iteration( + codegen_plugin_context& ctx, + const common_vars& names ) -> void { - using ecsact::cc_lang_support::cpp_identifier; - using ecsact::cpp_codegen_plugin_util::block; - using ecsact::meta::component_name; - using ecsact::meta::decl_full_name; - using ecsact::meta::get_child_system_ids; - using ecsact::rt_entt_codegen::ecsact_entt_system_details; - using ecsact::rt_entt_codegen::other_key; - using ecsact::rt_entt_codegen::system_util::create_context_struct_name; - using ecsact::rt_entt_codegen::system_util::create_context_var_name; - using ecsact::rt_entt_codegen::system_util::get_unique_view_name; - using ecsact::rt_entt_codegen::util::method_printer; - - for(auto& assoc_detail : system_details.association_details) { - auto struct_name = create_context_struct_name(assoc_detail.component_id); - auto context_name = create_context_var_name(assoc_detail.component_id); - - auto struct_header = struct_name + " : ecsact_system_execution_context "; +} - auto view_type_name = get_unique_view_name(); - other_view_names.insert( - other_view_names.end(), - std::pair( - other_key{ - .component_like_id = assoc_detail.component_id, - .field_id = assoc_detail.field_id // - }, - view_type_name - ) - ); +auto provider::association::pre_exec_system_impl( + codegen_plugin_context& ctx, + const common_vars& names +) -> void { +} - auto other_details = - ecsact_entt_system_details::from_capabilities(assoc_detail.capabilities); +auto provider::association::system_impl( + codegen_plugin_context& ctx, + const common_vars& names +) -> handle_exclusive_provide { + ctx.write("system_impl(&context);\n"); + return HANDLED; +} - ecsact::rt_entt_codegen::util::make_view( - ctx, - view_type_name, - "registry", - other_details - ); +auto provider::association::print_other_contexts( + codegen_plugin_context& ctx, + const common_vars& names +) -> void { + auto assoc_ids = ecsact::meta::system_assoc_ids(sys_like_id); + for(auto assoc_id : assoc_ids) { + auto assoc_comp_id = + ecsact::meta::system_assoc_component_id(sys_like_id, assoc_id); + auto struct_name = get_assoc_context_type_name(sys_like_id, assoc_id); + auto context_name = get_assoc_context_var_name(sys_like_id, assoc_id); + auto struct_header = struct_name + " : ecsact_system_execution_context "; + auto assoc_caps = + ecsact::meta::system_assoc_capabilities(sys_like_id, assoc_id); + auto assoc_system_details = + ecsact_entt_system_details::from_capabilities(assoc_caps); ctx.write(std::format( - "using {}_t = decltype({});\n", - view_type_name, - view_type_name + "using {0}_t = decltype({0});\n", + assoc_view_names.at(assoc_id) )); block(ctx, "struct " + struct_header, [&] { @@ -185,37 +323,58 @@ auto provider::association::print_other_contexts( using ecsact::rt_entt_codegen::util::decl_cpp_ident; using std::views::transform; - ctx.write(std::format("{}_t* view;\n", view_type_name)); + ctx.write(std::format("{}_t* view;\n", assoc_view_names.at(assoc_id))); + context_view_storage_struct_impl(ctx, assoc_caps); ctx.write("\n"); print_other_ctx_action(ctx); - print_other_ctx_add(ctx, assoc_detail.capabilities, other_details); + print_other_ctx_add( + ctx, + std::unordered_map{assoc_caps.begin(), assoc_caps.end()}, + assoc_system_details + ); print_other_ctx_remove( ctx, - assoc_detail.capabilities, - other_details, - view_type_name + std::unordered_map{assoc_caps.begin(), assoc_caps.end()}, + assoc_system_details, + assoc_view_names.at(assoc_id) + ); + print_other_ctx_get( + ctx, + assoc_system_details, + assoc_view_names.at(assoc_id) + ); + print_other_ctx_update( + ctx, + assoc_system_details, + assoc_view_names.at(assoc_id) ); - print_other_ctx_get(ctx, other_details, view_type_name); - print_other_ctx_update(ctx, other_details, view_type_name); - print_other_ctx_has(ctx, other_details); - print_other_ctx_generate(ctx, other_details); + print_other_ctx_has(ctx, assoc_system_details); + print_other_ctx_generate(ctx, assoc_system_details); print_other_ctx_parent(ctx, sys_like_id); - print_other_ctx_other(ctx, other_details); + print_other_ctx_other(ctx, assoc_system_details); }); ctx.write(";\n\n"); ctx.write(struct_name, " ", context_name, ";\n\n"); - ctx.write(context_name, ".view = &", view_type_name, ";\n"); + ctx.write(context_name, ".view = &", assoc_view_names.at(assoc_id), ";\n"); + for(auto&& [comp_id, _] : assoc_caps) { + auto comp_cpp_ident = cpp_identifier(decl_full_name(comp_id)); + ctx.write(std::format( + "{}.storage.c{} = {}.storage<{}>();\n", + context_name, + static_cast(comp_id), + assoc_view_names.at(assoc_id), + comp_cpp_ident + )); + } ctx.write(context_name, ".parent_ctx = nullptr;\n\n"); ctx.write(context_name, ".registry = &", names.registry_var_name, ";\n"); } } -using ecsact::rt_entt_codegen::util::method_printer; - -auto provider::association::print_other_ctx_action( - ecsact::codegen_plugin_context& ctx +auto provider::association::print_other_ctx_action( // + codegen_plugin_context& ctx ) -> void { auto printer = // method_printer{ctx, "action"} @@ -226,9 +385,9 @@ auto provider::association::print_other_ctx_action( } auto provider::association::print_other_ctx_add( - ecsact::codegen_plugin_context& ctx, - const capability_t& other_caps, - const ecsact::rt_entt_codegen::ecsact_entt_system_details& details + codegen_plugin_context& ctx, + const capability_t& other_caps, + const ecsact_entt_system_details& details ) -> void { auto printer = // method_printer{ctx, "add"} @@ -240,62 +399,78 @@ auto provider::association::print_other_ctx_add( } auto provider::association::print_other_ctx_remove( - ecsact::codegen_plugin_context& ctx, - const capability_t& other_caps, - const ecsact::rt_entt_codegen::ecsact_entt_system_details& details, - const std::string& view_type_name + codegen_plugin_context& ctx, + const capability_t& other_caps, + const ecsact_entt_system_details& details, + const std::string& view_type_name ) -> void { auto printer = // method_printer{ctx, "remove"} .parameter("ecsact_component_like_id", "component_id") + .parameter( + "ecsact::entt::detail::assoc_hash_value_t", + "assoc_fields_hash" + ) .return_type("void final"); context_remove_impl(ctx, other_caps, details, view_type_name); } auto provider::association::print_other_ctx_get( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::ecsact_entt_system_details& details, - const std::string& view_type_name + codegen_plugin_context& ctx, + const ecsact_entt_system_details& details, + const std::string& view_type_name ) -> void { auto printer = // method_printer{ctx, "get"} .parameter("ecsact_component_like_id", "component_id") .parameter("void*", "out_component_data") + .parameter( + "ecsact::entt::detail::assoc_hash_value_t", + "assoc_fields_hash" + ) .return_type("void final"); context_get_impl(ctx, sys_like_id, details, view_type_name); } auto provider::association::print_other_ctx_update( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::ecsact_entt_system_details& details, - const std::string& view_type_name + codegen_plugin_context& ctx, + const ecsact_entt_system_details& details, + const std::string& view_type_name ) -> void { auto printer = // method_printer{ctx, "update"} .parameter("ecsact_component_like_id", "component_id") .parameter("const void*", "component_data") + .parameter( + "ecsact::entt::detail::assoc_hash_value_t", + "assoc_fields_hash" + ) .return_type("void final"); context_update_impl(ctx, sys_like_id, details, view_type_name); } auto provider::association::print_other_ctx_has( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::ecsact_entt_system_details& details + codegen_plugin_context& ctx, + const ecsact_entt_system_details& details ) -> void { auto printer = // method_printer{ctx, "has"} .parameter("ecsact_component_like_id", "component_id") + .parameter( + "ecsact::entt::detail::assoc_hash_value_t", + "assoc_fields_hash" + ) .return_type("bool final"); context_has_impl(ctx, sys_like_id, details); } auto provider::association::print_other_ctx_generate( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::ecsact_entt_system_details& details + codegen_plugin_context& ctx, + const ecsact_entt_system_details& details ) -> void { auto printer = // method_printer{ctx, "generate"} @@ -308,8 +483,8 @@ auto provider::association::print_other_ctx_generate( } auto provider::association::print_other_ctx_parent( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::system_like_id_variant& sys_like_id + codegen_plugin_context& ctx, + const system_like_id_variant& sys_like_id ) -> void { auto printer = // method_printer{ctx, "parent"} // @@ -319,12 +494,12 @@ auto provider::association::print_other_ctx_parent( } auto provider::association::print_other_ctx_other( - ecsact::codegen_plugin_context& ctx, - const ecsact::rt_entt_codegen::ecsact_entt_system_details& details + codegen_plugin_context& ctx, + const ecsact_entt_system_details& details ) -> void { auto printer = // method_printer{ctx, "other"} - .parameter("ecsact_entity_id", "entity") + .parameter("ecsact_system_assoc_id", "assoc_id") .return_type("ecsact_system_execution_context* final"); context_other_impl(ctx, sys_like_id, details); diff --git a/rt_entt_codegen/core/system_provider/association/association.hh b/rt_entt_codegen/core/system_provider/association/association.hh index 834098e..173e651 100644 --- a/rt_entt_codegen/core/system_provider/association/association.hh +++ b/rt_entt_codegen/core/system_provider/association/association.hh @@ -2,6 +2,7 @@ #include #include +#include #include "rt_entt_codegen/core/system_provider/system_provider.hh" #include "rt_entt_codegen/core/sys_exec/sys_exec.hh" #include "rt_entt_codegen/core/system_provider/system_ctx_functions.hh" @@ -11,89 +12,110 @@ class association final : public system_provider { public: using system_provider::system_provider; + auto initialization( // + codegen_plugin_context& ctx, + const common_vars& names + ) -> void final; + + auto entity_iteration( + codegen_plugin_context& ctx, + const common_vars& names, + std::function iter_func + ) -> handle_exclusive_provide final; + + auto before_make_view_or_group( + ecsact::codegen_plugin_context& ctx, + const ecsact::rt_entt_codegen::core::common_vars& names, + std::vector& additional_view_components + ) -> void final; + auto after_make_view_or_group( - ecsact::codegen_plugin_context& ctx, - const common_vars& names + codegen_plugin_context& ctx, + const common_vars& names ) -> void final; auto context_function_header( - ecsact::codegen_plugin_context& ctx, - const common_vars& names + codegen_plugin_context& ctx, + const common_vars& names ) -> void final; auto context_function_other( - ecsact::codegen_plugin_context& ctx, - const common_vars& names + codegen_plugin_context& ctx, + const common_vars& names ) -> handle_exclusive_provide final; auto pre_entity_iteration( - ecsact::codegen_plugin_context& ctx, - const common_vars& names + codegen_plugin_context& ctx, + const common_vars& names ) -> void final; auto pre_exec_system_impl( - ecsact::codegen_plugin_context& ctx, - const common_vars& names + codegen_plugin_context& ctx, + const common_vars& names ) -> void final; - auto system_impl( - ecsact::codegen_plugin_context& ctx, - const common_vars& names + auto system_impl( // + codegen_plugin_context& ctx, + const common_vars& names ) -> handle_exclusive_provide final; private: - std::map other_view_names; + std::map assoc_view_names; + std::map> assoc_fields; - std::map components_with_entity_fields; + // List of composites the association needs to read from in their view/group + // due to indexed fields. + std::map> + assoc_composites; auto print_other_contexts( ecsact::codegen_plugin_context& ctx, const common_vars& names ) -> void; - auto print_other_ctx_action(ecsact::codegen_plugin_context& ctx) -> void; + auto print_other_ctx_action(codegen_plugin_context& ctx) -> void; auto print_other_ctx_add( - ecsact::codegen_plugin_context& ctx, + codegen_plugin_context& ctx, const capability_t& other_caps, const ecsact_entt_system_details& details ) -> void; auto print_other_ctx_remove( - ecsact::codegen_plugin_context& ctx, + codegen_plugin_context& ctx, const capability_t& other_caps, const ecsact_entt_system_details& details, const std::string& view_type_name ) -> void; auto print_other_ctx_get( - ecsact::codegen_plugin_context& ctx, + codegen_plugin_context& ctx, const ecsact_entt_system_details& details, const std::string& view_type_name ) -> void; auto print_other_ctx_update( - ecsact::codegen_plugin_context& ctx, + codegen_plugin_context& ctx, const ecsact_entt_system_details& details, const std::string& view_type_name ) -> void; auto print_other_ctx_has( - ecsact::codegen_plugin_context& ctx, + codegen_plugin_context& ctx, const ecsact_entt_system_details& details ) -> void; auto print_other_ctx_generate( - ecsact::codegen_plugin_context& ctx, + codegen_plugin_context& ctx, const ecsact_entt_system_details& details ) -> void; auto print_other_ctx_parent( - ecsact::codegen_plugin_context& ctx, - const system_like_id_variant& sys_like_id + codegen_plugin_context& ctx, + const system_like_id_variant& sys_like_id ) -> void; auto print_other_ctx_other( - ecsact::codegen_plugin_context& ctx, + codegen_plugin_context& ctx, const ecsact_entt_system_details& details ) -> void; }; diff --git a/rt_entt_codegen/core/system_provider/basic/basic.cc b/rt_entt_codegen/core/system_provider/basic/basic.cc index 344f3ee..37509e4 100644 --- a/rt_entt_codegen/core/system_provider/basic/basic.cc +++ b/rt_entt_codegen/core/system_provider/basic/basic.cc @@ -6,6 +6,9 @@ #include "ecsact/cpp_codegen_plugin_util.hh" #include "rt_entt_codegen/core/system_provider/system_ctx_functions.hh" +using ecsact::cc_lang_support::cpp_identifier; +using ecsact::meta::decl_full_name; + using namespace ecsact::rt_entt_codegen::core; auto provider::basic::initialization( @@ -110,6 +113,47 @@ auto provider::basic::system_impl( return HANDLED; } +template +static auto get_assoc_fields(CompositeID compo_id +) -> std::vector { + auto result = std::vector{}; + + for(auto field_id : ecsact::meta::get_field_ids(compo_id)) { + auto field_type = ecsact::meta::get_field_type(compo_id, field_id); + if(field_type.kind == ECSACT_TYPE_KIND_BUILTIN && + field_type.type.builtin == ECSACT_ENTITY_TYPE) { + result.push_back(field_id); + } + + if(field_type.kind == ECSACT_TYPE_KIND_FIELD_INDEX) { + result.push_back(field_id); + } + } + + return result; +} + +static auto has_assoc_fields(ecsact::rt_entt_codegen::system_like_id_variant id +) -> bool { + for(auto&& [comp_id, _] : ecsact::meta::system_capabilities_list(id)) { + if(!get_assoc_fields(comp_id).empty()) { + return true; + } + } + return false; +} + +static auto get_first_assoc_comp( + ecsact::rt_entt_codegen::system_like_id_variant id +) -> std::optional { + for(auto&& [comp_id, _] : ecsact::meta::system_capabilities_list(id)) { + if(!get_assoc_fields(comp_id).empty()) { + return comp_id; + } + } + return {}; +} + auto provider::basic::entity_iteration( ecsact::codegen_plugin_context& ctx, const ecsact::rt_entt_codegen::core::common_vars& names, @@ -118,6 +162,26 @@ auto provider::basic::entity_iteration( using ecsact::cpp_codegen_plugin_util::block; block(ctx, "for(ecsact::entt::entity_id entity : view)", [&] { + auto assoc_multi_view_block = + std::optional{}; + + if(has_assoc_fields(sys_like_id)) { + ctx.write(std::format( // + "for(auto storage_id : " + "view.get>(entity)." + "storage_hash_value_ids)", + cpp_identifier(decl_full_name(get_first_assoc_comp(sys_like_id).value()) + ) + )); + assoc_multi_view_block.emplace(ctx); + ctx.write(std::format( + "context.storage.c{} = ®istry.storage<{}>(storage_id);", + static_cast(get_first_assoc_comp(sys_like_id).value()), + cpp_identifier(decl_full_name(get_first_assoc_comp(sys_like_id).value()) + ) + )); + } + iter_func(); }); return HANDLED; @@ -145,6 +209,21 @@ auto provider::basic::provide_context_init( ); ctx.write("context.parent_ctx = ", names.parent_context_var_name, ";\n"); ctx.write("context.view = &view;\n\n"); + for(auto&& [comp_id, _] : + ecsact::meta::system_capabilities_list(sys_like_id)) { + // TODO: Replace this garbage code. just for POC + if(get_assoc_fields(comp_id).empty()) { + continue; + } + + auto comp_cpp_ident = cpp_identifier(decl_full_name(comp_id)); + ctx.write(std::format( + "context.storage.c{} = view.storage<{}>();\n", + static_cast(comp_id), + comp_cpp_ident + )); + } + return HANDLED; } diff --git a/rt_entt_codegen/core/system_provider/lazy/lazy.cc b/rt_entt_codegen/core/system_provider/lazy/lazy.cc index e5e9c89..e0b6547 100644 --- a/rt_entt_codegen/core/system_provider/lazy/lazy.cc +++ b/rt_entt_codegen/core/system_provider/lazy/lazy.cc @@ -117,12 +117,12 @@ auto provider::lazy::post_iteration( ">([](const auto& a, const auto& b) { return a.hash < b.hash; });\n" ); - ecsact::rt_entt_codegen::util::make_view( - ctx, - "view_no_pending_lazy_", - names.registry_var_name, - system_details - ); + auto make_view_opts = util::make_view_options(system_details); + make_view_opts.view_var_name = "view_no_pending_lazy_"; + make_view_opts.registry_var_name = names.registry_var_name; + make_view_opts.sys_like_id = sys_like_id; + + ecsact::rt_entt_codegen::util::make_view(ctx, make_view_opts); ctx.write("auto view_no_pending_lazy_count_ = 0;\n"); 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 3e40980..da66bc8 100644 --- a/rt_entt_codegen/core/system_provider/system_ctx_functions.cc +++ b/rt_entt_codegen/core/system_provider/system_ctx_functions.cc @@ -10,6 +10,25 @@ using ecsact::cpp_codegen_plugin_util::block; using ecsact::meta::decl_full_name; using ecsact::rt_entt_codegen::util::is_transient_component; +auto ecsact::rt_entt_codegen::core::provider::context_view_storage_struct_impl( + ecsact::codegen_plugin_context& ctx, + std::vector> + system_caps +) -> void { + block(ctx, "struct", [&] { + for(auto i = 0; system_caps.size() > i; ++i) { + auto comp_id = system_caps.at(i).first; + auto comp_cpp_ident = cpp_identifier(decl_full_name(comp_id)); + ctx.write(std::format( + "::entt::basic_storage<{}>* c{} = nullptr;\n", + comp_cpp_ident, + static_cast(comp_id) + )); + } + }); + ctx.write(" storage;\n"); +} + auto ecsact::rt_entt_codegen::core::provider::context_action_impl( ecsact::codegen_plugin_context& ctx, const system_like_id_variant& sys_like_id @@ -118,7 +137,7 @@ auto ecsact::rt_entt_codegen::core::provider::context_remove_impl( type_name, ">(this, ecsact_id_cast(", type_name, - "::id), *view);\n" + "::id), *view, assoc_fields_hash);\n" ); return; } @@ -148,7 +167,7 @@ auto ecsact::rt_entt_codegen::core::provider::context_remove_impl( ctx.write("return result;\n"); }); - ctx.write("();\n"); + ctx.write("(assoc_fields_hash);\n"); } auto ecsact::rt_entt_codegen::core::provider::context_get_impl( @@ -184,19 +203,19 @@ auto ecsact::rt_entt_codegen::core::provider::context_get_impl( cpp_comp_full_name, "::id) == component_id);\n" ); - ctx.write( - "*static_cast<::", - cpp_comp_full_name, - "*>(out_component_data) = view->get<::", + ctx.write(std::format( + "*static_cast<::{}*>(out_component_data) = " + "storage.c{}->get(entity);", cpp_comp_full_name, - ">(entity);" - ); + static_cast(comp_id) + )); return; } 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 *, {}_t&, " + "ecsact::entt::detail::assoc_hash_value_t);\n", view_type_name )); @@ -223,7 +242,8 @@ 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, *view, " + "assoc_fields_hash);\n" ); } @@ -246,14 +266,15 @@ auto ecsact::rt_entt_codegen::core::provider::context_update_impl( ">(this, ecsact_id_cast(", type_name, "::id),", - "component_data, *view); \n" + "component_data, *view, assoc_fields_hash); \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 *, {}_t&, " + "ecsact::entt::detail::assoc_hash_value_t);\n", view_type_name )); @@ -281,7 +302,7 @@ auto ecsact::rt_entt_codegen::core::provider::context_update_impl( ctx.write( "update_fns.at(component_id)(this, component_id, component_data, " - "*view);\n" + "*view, assoc_fields_hash);\n" ); } @@ -303,14 +324,15 @@ auto ecsact::rt_entt_codegen::core::provider::context_has_impl( type_name, ">(this, ecsact_id_cast(", type_name, - "::id));\n" + "::id), assoc_fields_hash);" ); + return; } block( ctx, "static const auto has_fns = " "std::unordered_map", + "decltype(&wrapper::dynamic::context_has)>", [&] { for(const auto comp_id : details.readable_comps) { auto type_name = cpp_identifier(decl_full_name(comp_id)); @@ -326,7 +348,9 @@ 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, assoc_fields_hash);" + ); } auto ecsact::rt_entt_codegen::core::provider::context_generate_impl( @@ -402,10 +426,23 @@ auto ecsact::rt_entt_codegen::core::provider::context_other_impl( return; } - ctx.write( - "if(other_contexts.contains(entity)) {\n", - "return other_contexts.at(entity);\n}\n" - ); + if(details.association_details.size() == 1) { + ctx.write( + "// system has only 1 association. No other association ID should be " + "passed in here\n" + ); + ctx.write("assert(static_cast(assoc_id) == 0);\n"); + ctx.write("return other_contexts[0];"); + return; + } - ctx.write("return nullptr;\n"); + ctx.write(std::format( + "// system as {} associations and assoc_id is assumed to be an index\n", + details.association_details.size() + )); + ctx.write(std::format( + "assert(static_cast(assoc_id) < {});\n", + details.association_details.size() + )); + ctx.write("return other_contexts[static_cast(assoc_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..88cfa87 100644 --- a/rt_entt_codegen/core/system_provider/system_ctx_functions.hh +++ b/rt_entt_codegen/core/system_provider/system_ctx_functions.hh @@ -8,6 +8,12 @@ namespace ecsact::rt_entt_codegen::core::provider { using capability_t = std::unordered_map; +auto context_view_storage_struct_impl( // + ecsact::codegen_plugin_context& ctx, + std::vector> + caps +) -> void; + auto context_action_impl( ecsact::codegen_plugin_context& ctx, const system_like_id_variant& sys_like_id diff --git a/rt_entt_codegen/core/update_indexed_storage.cc b/rt_entt_codegen/core/update_indexed_storage.cc new file mode 100644 index 0000000..bd77745 --- /dev/null +++ b/rt_entt_codegen/core/update_indexed_storage.cc @@ -0,0 +1,47 @@ + +#include "core.hh" + +#include +#include "ecsact/lang-support/lang-cc.hh" +#include "rt_entt_codegen/shared/util.hh" +#include "ecsact/runtime/common.h" +#include "ecsact/cpp_codegen_plugin_util.hh" + +using ecsact::codegen_plugin_context; +using ecsact::cc_lang_support::cpp_identifier; +using ecsact::cpp_codegen_plugin_util::method_printer; +using ecsact::meta::decl_full_name; +using ecsact::rt_entt_codegen::ecsact_entt_details; + +static auto print_update_indexed_storage_component( + codegen_plugin_context& ctx, + const ecsact_entt_details& details, + ecsact_component_like_id comp_like_id +) -> void { + auto comp_cpp_ident = cpp_identifier(decl_full_name(comp_like_id)); + + ctx.write("template<> "); + auto printer = + method_printer{ + ctx, + std::format( + "ecsact::entt::detail::update_indexed_storage<{}>", + comp_cpp_ident + ) + } + .parameter("ecsact::entt::registry_t&", "reg") + .return_type("void"); +} + +auto ecsact::rt_entt_codegen::core::print_update_indexed_storage( + codegen_plugin_context& ctx, + const ecsact_entt_details& details +) -> void { + for(auto comp_id : details.all_components) { + print_update_indexed_storage_component( + ctx, + details, + ecsact_id_cast(comp_id) + ); + } +} diff --git a/rt_entt_codegen/rt_entt_codegen.cc b/rt_entt_codegen/rt_entt_codegen.cc index a97ee07..7dac966 100644 --- a/rt_entt_codegen/rt_entt_codegen.cc +++ b/rt_entt_codegen/rt_entt_codegen.cc @@ -50,11 +50,12 @@ void ecsact_codegen_plugin( inc_header(ctx, "ecsact/entt/execution.hh"); inc_header(ctx, "ecsact/entt/registry_util.hh"); inc_header(ctx, "ecsact/entt/detail/globals.hh"); + inc_header(ctx, "ecsact/entt/detail/assoc_fields_hash.hh"); 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/hash.hh"); + inc_header(ctx, "ecsact/entt/detail/indexed_storage.hh"); inc_header(ctx, "ecsact/entt/wrapper/core.hh"); inc_header(ctx, "ecsact/entt/wrapper/dynamic.hh"); inc_header(ctx, "ecsact/entt/error_check.hh"); @@ -74,6 +75,13 @@ void ecsact_codegen_plugin( "ecsact::entt::actions_map&>;\n\n" ); + ctx.write( + "static_assert(sizeof(::entt::id_type) == " + "sizeof(ecsact::entt::detail::assoc_hash_value_t),\"EnTT " + "storage id type must match the size of ecsact_rt_entt internal hash " + "algorithm size\");\n\n" + ); + init_global(ctx, "registries"); init_global(ctx, "last_registry_id"); init_global(ctx, "system_impls"); @@ -234,6 +242,9 @@ void ecsact_codegen_plugin( { // Print core Ecsact API methods using namespace ecsact::rt_entt_codegen; + core::print_assoc_fields_hash(ctx, details); + core::print_assoc_globals(ctx, details); + core::print_update_indexed_storage(ctx, details); core::print_entity_match_fn(ctx, details); core::print_system_marker_add_fn(ctx, details); core::print_system_marker_remove_fn(ctx, details); diff --git a/rt_entt_codegen/shared/BUILD.bazel b/rt_entt_codegen/shared/BUILD.bazel index 0147e6b..eff6170 100644 --- a/rt_entt_codegen/shared/BUILD.bazel +++ b/rt_entt_codegen/shared/BUILD.bazel @@ -32,10 +32,12 @@ cc_library( cc_library( name = "util", hdrs = ["util.hh"], + srcs = ["util.cc"], copts = copts, defines = ["ECSACT_META_API_LOAD_AT_RUNTIME"], deps = [ ":comps_with_caps", + ":system_variant", "@ecsact_codegen//:plugin", "@ecsact_lang_cpp//:support", "@ecsact_runtime//:common", diff --git a/rt_entt_codegen/shared/comps_with_caps.hh b/rt_entt_codegen/shared/comps_with_caps.hh index 087f9cb..6a64e34 100644 --- a/rt_entt_codegen/shared/comps_with_caps.hh +++ b/rt_entt_codegen/shared/comps_with_caps.hh @@ -21,21 +21,19 @@ auto get_all_deep_capabilities( // all_capabilities[comp_id] = static_cast( capability | all_capabilities[comp_id] ); + } - auto fields = - ecsact::meta::system_association_fields(system_like_id, comp_id); - for(auto field_id : fields) { - auto assoc_comps = ecsact::meta::system_association_capabilities( - system_like_id, - comp_id, - field_id - ); + auto assoc_ids = ecsact::meta::system_assoc_ids(system_like_id); + for(auto assoc_id : assoc_ids) { + auto assoc_comp = + ecsact::meta::system_assoc_component_id(system_like_id, assoc_id); + auto assoc_comp_caps = + ecsact::meta::system_assoc_capabilities(system_like_id, assoc_id); - for(auto&& [assoc_comp_id, assoc_comp_cap] : assoc_comps) { - all_capabilities[assoc_comp_id] = static_cast( - assoc_comp_cap | all_capabilities[assoc_comp_id] - ); - } + for(auto&& [comp_id, capability] : assoc_comp_caps) { + all_capabilities[comp_id] = static_cast( + capability | all_capabilities[comp_id] + ); } } diff --git a/rt_entt_codegen/shared/ecsact_entt_details.cc b/rt_entt_codegen/shared/ecsact_entt_details.cc index 219d02c..bc76c2d 100644 --- a/rt_entt_codegen/shared/ecsact_entt_details.cc +++ b/rt_entt_codegen/shared/ecsact_entt_details.cc @@ -1,6 +1,7 @@ #include "ecsact_entt_details.hh" #include +#include #include "ecsact/codegen/plugin.h" #include "ecsact/codegen/plugin.hh" #include "ecsact/lang-support/lang-cc.hh" @@ -80,6 +81,30 @@ static auto collect_all_actions( // } } +auto ecsact_entt_system_details::get_all_writable_comps() const + -> std::vector { + auto comp_ids = std::vector{ + writable_comps.begin(), + writable_comps.end(), + }; + + for(auto assoc_details : association_details) { + for(auto&& [comp_id, caps] : assoc_details.capabilities) { + if((caps & ECSACT_SYS_CAP_WRITEONLY) != ECSACT_SYS_CAP_WRITEONLY) { + continue; + } + + if(std::ranges::find(comp_ids, comp_id) != comp_ids.end()) { + continue; + } + + comp_ids.emplace_back(comp_id); + } + } + + return comp_ids; +} + auto ecsact_entt_system_details::fill_system_details( ecsact_entt_system_details& out_details, const std::unordered_map& @@ -140,24 +165,22 @@ auto ecsact_entt_system_details::from_system_like( // fill_system_details(details, caps); - for(auto comp_id : details.readable_comps) { - auto fields = ecsact::meta::system_association_fields(sys_like_id, comp_id); - for(auto field_id : fields) { - auto assoc_comps = ecsact::meta::system_association_capabilities( - sys_like_id, - comp_id, - field_id - ); - - details.association_details.insert( - details.association_details.end(), - association_info{ - .component_id = comp_id, - .field_id = field_id, - .capabilities = assoc_comps, - } - ); - } + for(auto assoc_id : ecsact::meta::system_assoc_ids(sys_like_id)) { + auto assoc_comp_id = + ecsact::meta::system_assoc_component_id(sys_like_id, assoc_id); + auto assoc_fields = + ecsact::meta::system_assoc_fields(sys_like_id, assoc_id); + auto assoc_capabilities = + ecsact::meta::system_assoc_capabilities(sys_like_id, assoc_id); + + details.association_details.insert( + details.association_details.end(), + association_info{ + assoc_comp_id, + assoc_fields, + assoc_capabilities, + } + ); } auto generate_ids = ecsact::meta::get_system_generates_ids(sys_like_id); @@ -182,6 +205,13 @@ auto ecsact_entt_system_details::from_capabilities( // return details; } +auto ecsact_entt_system_details::from_capabilities( + std::vector> + caps +) -> ecsact_entt_system_details { + return from_capabilities(std::unordered_map{caps.begin(), caps.end()}); +} + auto ecsact_entt_details::from_package( // ecsact_package_id pkg_id ) -> ecsact_entt_details { diff --git a/rt_entt_codegen/shared/ecsact_entt_details.hh b/rt_entt_codegen/shared/ecsact_entt_details.hh index cdcd316..9069e37 100644 --- a/rt_entt_codegen/shared/ecsact_entt_details.hh +++ b/rt_entt_codegen/shared/ecsact_entt_details.hh @@ -8,9 +8,9 @@ namespace ecsact::rt_entt_codegen { struct association_info { - ecsact_component_like_id component_id; - ecsact_field_id field_id; - std::unordered_map + ecsact_component_like_id component_id; + std::vector field_ids; + std::vector> capabilities; }; @@ -52,6 +52,8 @@ struct ecsact_entt_system_details { * requirements*/ generate_t generate_comps; + auto get_all_writable_comps() const -> std::vector; + static auto from_system_like( // ecsact_system_like_id sys_like_id ) -> ecsact_entt_system_details; @@ -60,6 +62,11 @@ struct ecsact_entt_system_details { std::unordered_map caps ) -> ecsact_entt_system_details; + static auto from_capabilities( + std::vector> + caps + ) -> ecsact_entt_system_details; + private: static auto fill_system_details( ecsact_entt_system_details& in_details, diff --git a/rt_entt_codegen/shared/parallel.cc b/rt_entt_codegen/shared/parallel.cc index 7caa4d5..8fd9058 100644 --- a/rt_entt_codegen/shared/parallel.cc +++ b/rt_entt_codegen/shared/parallel.cc @@ -7,7 +7,6 @@ #include "ecsact/lang-support/lang-cc.hh" #include "rt_entt_codegen/shared/system_variant.hh" -#include "system_variant.hh" #include "ecsact/runtime/meta.hh" using ecsact::rt_entt_codegen::system_like_id_variant; @@ -162,15 +161,13 @@ auto ecsact::rt_entt_codegen::parallel::can_entities_parallel( return false; } - auto other_fields = - ecsact::meta::system_association_fields(sys_like_id, comp_id); + auto assoc_ids = ecsact::meta::system_assoc_ids(sys_like_id); - for(auto field_id : other_fields) { - auto other_capabilities = ecsact::meta::system_association_capabilities( - sys_like_id, - comp_id, - field_id - ); + for(auto assoc_id : assoc_ids) { + auto assoc_comp_id = + ecsact::meta::system_assoc_component_id(sys_like_id, assoc_id); + auto other_capabilities = + ecsact::meta::system_assoc_capabilities(sys_like_id, assoc_id); // NOTE(Kelwan): Association is currently not compatible with executing // entities in parallel. @@ -253,6 +250,7 @@ static auto loop_iterator( loop_iterator(system_list, iterator, parallel_system_cluster); return; } else { + // TODO: check if capability safe child_unsafe_comps.insert(child_comp_id); } } @@ -280,27 +278,27 @@ static auto loop_iterator( 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; - } + auto assoc_ids = ecsact::meta::system_assoc_ids(sys_like_id); + for(auto assoc_id : assoc_ids) { + auto assoc_comp_id = + ecsact::meta::system_assoc_component_id(sys_like_id, assoc_id); + auto assoc_field_ids = + ecsact::meta::system_assoc_fields(sys_like_id, assoc_id); + auto assoc_capabilities = + ecsact::meta::system_assoc_capabilities(sys_like_id, assoc_id); + + for(const auto [other_comp_id, other_capability] : assoc_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 { + assert(!parallel_system_list.empty()); + parallel_system_cluster.push_back(parallel_system_list); + loop_iterator(system_list, iterator, parallel_system_cluster); + return; } } } diff --git a/rt_entt_codegen/shared/parallel.hh b/rt_entt_codegen/shared/parallel.hh index 7e163df..3e0ff1d 100644 --- a/rt_entt_codegen/shared/parallel.hh +++ b/rt_entt_codegen/shared/parallel.hh @@ -2,7 +2,7 @@ #include "ecsact/codegen/plugin.hh" #include "rt_entt_codegen/shared/ecsact_entt_details.hh" -#include "system_variant.hh" +#include "rt_entt_codegen/shared/system_variant.hh" namespace ecsact::rt_entt_codegen::parallel { auto get_parallel_execution_cluster( diff --git a/rt_entt_codegen/shared/system_util.cc b/rt_entt_codegen/shared/system_util.cc index 1dc8da3..b4e77dc 100644 --- a/rt_entt_codegen/shared/system_util.cc +++ b/rt_entt_codegen/shared/system_util.cc @@ -1,10 +1,15 @@ #include "system_util.hh" #include +#include #include #include "ecsact/runtime/common.h" +using namespace ecsact::rt_entt_codegen; +using ecsact::cc_lang_support::c_identifier; +using ecsact::meta::decl_full_name; + using capability_t = std::unordered_map; @@ -74,3 +79,22 @@ auto ecsact::rt_entt_codegen::system_util::get_unique_view_name() static int counter = 0; return "view" + std::to_string(counter++); } + +auto system_util::get_assoc_context_type_name( // + system_like_id_variant sys_like_id, + ecsact_system_assoc_id assoc_id +) -> std::string { + return std::format( + "{}__Context__{}", + c_identifier(decl_full_name(sys_like_id)), + static_cast(assoc_id) + ); +} + +auto system_util::get_assoc_context_var_name( // + system_like_id_variant, + ecsact_system_assoc_id assoc_id +) -> std::string { + // sys_like_id not used here - kept for potential future use + return std::format("assoc_context{}_", static_cast(assoc_id)); +} diff --git a/rt_entt_codegen/shared/system_util.hh b/rt_entt_codegen/shared/system_util.hh index 0b51ecd..8dea9bb 100644 --- a/rt_entt_codegen/shared/system_util.hh +++ b/rt_entt_codegen/shared/system_util.hh @@ -2,7 +2,7 @@ #include "ecsact/runtime/meta.hh" #include "ecsact/lang-support/lang-cc.hh" -#include "rt_entt_codegen/shared/system_variant.hh" +#include "system_variant.hh" namespace ecsact::rt_entt_codegen::system_util { @@ -19,24 +19,14 @@ auto is_trivial_system(ecsact_system_like_id system_id) -> bool; auto get_unique_view_name() -> std::string; -template -static auto create_context_struct_name( // - ComponentLikeID component_like_id -) -> std::string { - using ecsact::cc_lang_support::c_identifier; - auto full_name = - c_identifier(ecsact::meta::decl_full_name(component_like_id)); - return full_name + "Struct"; -} - -template -static auto create_context_var_name( // - ComponentLikeID component_like_id -) -> std::string { - using ecsact::cc_lang_support::c_identifier; - auto full_name = - c_identifier(ecsact::meta::decl_full_name(component_like_id)); - return full_name + "_context"; -} +auto get_assoc_context_type_name( // + system_like_id_variant sys_like_id, + ecsact_system_assoc_id assoc_id +) -> std::string; + +auto get_assoc_context_var_name( // + system_like_id_variant sys_like_id, + ecsact_system_assoc_id assoc_id +) -> std::string; } // namespace ecsact::rt_entt_codegen::system_util diff --git a/rt_entt_codegen/shared/util.cc b/rt_entt_codegen/shared/util.cc new file mode 100644 index 0000000..ffe1f16 --- /dev/null +++ b/rt_entt_codegen/shared/util.cc @@ -0,0 +1,138 @@ +#include "util.hh" + +#include + +using namespace std::string_literals; +using ecsact::cc_lang_support::cpp_identifier; +using ecsact::meta::decl_full_name; +using std::views::transform; + +auto ecsact::rt_entt_codegen::util::make_view( // + ecsact::codegen_plugin_context& ctx, + make_view_options opts +) -> void { + ctx.write( + "auto ", + opts.view_var_name, + " = ", + opts.registry_var_name, + ".view<" + ); + + // components that may have multiple instances + auto multi_components = std::vector{}; + auto is_multi_component = [&](auto id) { + auto itr = std::ranges::find( // + multi_components, + ecsact_id_cast(id) + ); + return itr != multi_components.end(); + }; + + if(!opts.without_multi_component_storage) { + for(auto assoc_info : opts.details.association_details) { + if(!is_multi_component(assoc_info.component_id)) { + multi_components.push_back(assoc_info.component_id); + } + } + } + + auto exclude_multi_components = [&] { + return std::views::filter([&](auto id) -> bool { + return !is_multi_component(id); + }); + }; + + ctx.write(comma_delim( + opts.details.get_comps | exclude_multi_components() | + transform(decl_cpp_ident) + )); + + if(!multi_components.empty() && + !(opts.details.get_comps | exclude_multi_components()).empty()) { + ctx.write(", "); + } + + ctx.write(comma_delim( + multi_components | // + transform([](auto id) -> std::string { + return std::format( + "ecsact::entt::detail::multi_assoc_storage<{}>", + decl_cpp_ident(id) + ); + }) + )); + + for(auto comp_id : opts.details.writable_comps | exclude_multi_components()) { + auto comp_name = decl_cpp_ident(comp_id); + + opts.additional_components.push_back(std::format( + "ecsact::entt::detail::exec_beforechange_storage<{}>", + comp_name + )); + } + + if(!opts.additional_components.empty()) { + ctx.write(", "); + ctx.write(comma_delim(opts.additional_components)); + } + + ctx.write(">("); + + auto exclude_comps = opts.details.exclude_comps | + transform(decl_cpp_ident); + + opts.additional_exclude_components.insert( + opts.additional_exclude_components.end(), + exclude_comps.begin(), + exclude_comps.end() + ); + + if(!opts.additional_exclude_components.empty()) { + ctx.write( + "::entt::exclude<", + comma_delim(opts.additional_exclude_components), + ">" + ); + } + + ctx.write(");\n"); + + if(opts.sys_like_id && opts.sys_like_id->is_action()) { + auto act_id = opts.sys_like_id->as_action(); + auto indexed_fields = std::map< + ecsact_composite_id, + std::vector>>{}; + for(auto field_id : ecsact::meta::get_field_ids(act_id)) { + auto act_field_name = ecsact::meta::field_name(act_id, field_id); + auto field_type = ecsact::meta::get_field_type(act_id, field_id); + if(field_type.kind != ECSACT_TYPE_KIND_FIELD_INDEX) { + continue; + } + + indexed_fields[field_type.type.field_index.composite_id].push_back({ + field_type.type.field_index.field_id, + std::format("action.{}", act_field_name), + }); + } + + for(auto&& [compo_id, fields] : indexed_fields) { + auto compo_name = cpp_identifier(decl_full_name(compo_id)); + auto hash_fields_str = std::format( + "::ecsact::entt::detail::hash_vals32(static_cast({}::id), " + "{})", + compo_name, + comma_delim(std::views::transform( + fields, + [](auto& entry) -> std::string { return entry.second; } + )) + ); + ctx.write(std::format( + "view.storage({}.storage<::ecsact::entt::indexed<{}>>({}));\n", + opts.registry_var_name, + compo_name, + hash_fields_str + )); + } + } +} diff --git a/rt_entt_codegen/shared/util.hh b/rt_entt_codegen/shared/util.hh index 02f22fe..71111c5 100644 --- a/rt_entt_codegen/shared/util.hh +++ b/rt_entt_codegen/shared/util.hh @@ -11,6 +11,7 @@ #include "ecsact/runtime/meta.h" #include "ecsact/runtime/meta.hh" #include "ecsact_entt_details.hh" +#include "rt_entt_codegen/shared/system_variant.hh" namespace ecsact::rt_entt_codegen::util { @@ -271,7 +272,32 @@ auto comma_delim(auto&& range) -> std::string { return result; } +struct make_view_options { + std::string_view view_var_name; + std::string_view registry_var_name; + std::vector additional_components; + std::vector additional_exclude_components; + std::optional sys_like_id; + bool without_multi_component_storage; + + const ecsact_entt_system_details& details; + + inline make_view_options(const ecsact_entt_system_details& details) + : details(details) { + } + + inline make_view_options(make_view_options&&) = default; + inline make_view_options(const make_view_options&) = default; + inline ~make_view_options() = default; +}; + auto make_view( // + ecsact::codegen_plugin_context& ctx, + make_view_options opts +) -> void; + +[[deprecated("use make_view() overload that uses make_view_options instead")]] +inline auto make_view( // ecsact::codegen_plugin_context& ctx, auto&& view_var_name, auto&& registry_var_name, @@ -279,51 +305,12 @@ auto make_view( // std::vector additional_components = {}, std::vector additional_exclude_components = {} ) -> void { - using namespace std::string_literals; - using ecsact::rt_entt_codegen::util::comma_delim; - using ecsact::rt_entt_codegen::util::decl_cpp_ident; - using std::views::transform; - - ctx.write("auto ", view_var_name, " = ", registry_var_name, ".view<"); - - ctx.write(comma_delim( - details.get_comps | transform(decl_cpp_ident) - )); - - for(auto comp_id : details.writable_comps) { - auto comp_name = decl_cpp_ident(comp_id); - - additional_components.push_back(std::format( - "ecsact::entt::detail::exec_beforechange_storage<{}>", - comp_name - )); - } - - if(!additional_components.empty()) { - ctx.write(", "); - ctx.write(comma_delim(additional_components)); - } - - ctx.write(">("); - - auto exclude_comps = details.exclude_comps | - transform(decl_cpp_ident); - - additional_exclude_components.insert( - additional_exclude_components.end(), - exclude_comps.begin(), - exclude_comps.end() - ); - - if(!additional_exclude_components.empty()) { - ctx.write( - "::entt::exclude<", - comma_delim(additional_exclude_components), - ">" - ); - } - - ctx.write(");\n"); + auto opts = make_view_options{details}; + opts.registry_var_name = registry_var_name; + opts.view_var_name = view_var_name; + opts.additional_components = additional_components; + opts.additional_exclude_components = additional_exclude_components; + return make_view(ctx, opts); } } // namespace ecsact::rt_entt_codegen::util diff --git a/runtime/ecsact_rt_entt_core.cc b/runtime/ecsact_rt_entt_core.cc index 8418c56..a676f8f 100644 --- a/runtime/ecsact_rt_entt_core.cc +++ b/runtime/ecsact_rt_entt_core.cc @@ -6,11 +6,21 @@ * derived from the input Ecsact files they will not be defined here. */ +#include #include "ecsact/runtime/core.h" #include "ecsact/entt/detail/globals.hh" +#include "ecsact/entt/detail/assoc_fields_hash.hh" #include "ecsact/entt/registry_util.hh" #include "ecsact/entt/entity.hh" +#ifdef __clang__ +static_assert(true, "workaround https://github.com/clangd/clangd/issues/1167"); +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Winconsistent-dllimport" +#endif + +using ecsact::entt::detail::get_assoc_fields_hash; + void ecsact_destroy_registry(ecsact_registry_id reg_id) { auto& reg = ecsact::entt::get_registry(reg_id); reg = {}; @@ -109,23 +119,39 @@ ecsact_add_error ecsact_add_component( bool ecsact_has_component( ecsact_registry_id reg_id, ecsact_entity_id entity_id, - ecsact_component_id component_id + ecsact_component_id component_id, + ... ) { using ecsact::entt::detail::globals::has_component_fns; + std::va_list indexed_fields; + va_start(indexed_fields, component_id); + auto assoc_fields_hash = get_assoc_fields_hash( + ecsact_id_cast(component_id), + indexed_fields + ); + va_end(indexed_fields); 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, assoc_fields_hash); } const void* ecsact_get_component( ecsact_registry_id reg_id, ecsact_entity_id entity_id, - ecsact_component_id component_id + ecsact_component_id component_id, + ... ) { using ecsact::entt::detail::globals::get_component_fns; + std::va_list indexed_fields; + va_start(indexed_fields, component_id); + auto assoc_fields_hash = get_assoc_fields_hash( + ecsact_id_cast(component_id), + indexed_fields + ); + va_end(indexed_fields); 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, assoc_fields_hash); } int ecsact_count_components( @@ -195,21 +221,47 @@ ecsact_update_error ecsact_update_component( ecsact_registry_id reg_id, ecsact_entity_id entity_id, ecsact_component_id component_id, - const void* component_data + const void* component_data, + ... ) { using ecsact::entt::detail::globals::update_component_fns; + std::va_list indexed_fields; + va_start(indexed_fields, component_data); + auto assoc_fields_hash = get_assoc_fields_hash( + ecsact_id_cast(component_id), + indexed_fields + ); + va_end(indexed_fields); 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, + assoc_fields_hash + ); } void ecsact_remove_component( ecsact_registry_id reg_id, ecsact_entity_id entity_id, - ecsact_component_id component_id + ecsact_component_id component_id, + ... ) { using ecsact::entt::detail::globals::remove_component_fns; + std::va_list indexed_fields; + va_start(indexed_fields, component_id); + auto assoc_fields_hash = get_assoc_fields_hash( + ecsact_id_cast(component_id), + indexed_fields + ); + va_end(indexed_fields); 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, assoc_fields_hash); } + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif diff --git a/runtime/ecsact_rt_entt_dynamic.cc b/runtime/ecsact_rt_entt_dynamic.cc index 8b5b829..6f919a6 100644 --- a/runtime/ecsact_rt_entt_dynamic.cc +++ b/runtime/ecsact_rt_entt_dynamic.cc @@ -6,12 +6,22 @@ * derived from the input Ecsact files they will not be defined here. */ +#include #include "ecsact/runtime/core.h" #include "ecsact/entt/detail/globals.hh" #include "ecsact/entt/registry_util.hh" #include "ecsact/entt/entity.hh" +#include "ecsact/entt/detail/assoc_fields_hash.hh" #include "ecsact/entt/detail/system_execution_context.hh" +#ifdef __clang__ +static_assert(true, "workaround https://github.com/clangd/clangd/issues/1167"); +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Winconsistent-dllimport" +#endif + +using ecsact::entt::detail::get_assoc_fields_hash; + bool ecsact_system_execution_context_same( const ecsact_system_execution_context* a, const ecsact_system_execution_context* b @@ -60,10 +70,9 @@ ecsact_system_like_id ecsact_system_execution_context_id( ecsact_system_execution_context* ecsact_system_execution_context_other( ecsact_system_execution_context* context, - ecsact_entity_id entity_id + ecsact_system_assoc_id assoc_id ) { - assert(context != nullptr); - return context->other(entity_id); + return context->other(assoc_id); } void ecsact_system_execution_context_add( @@ -77,36 +86,68 @@ void ecsact_system_execution_context_add( void ecsact_system_execution_context_remove( ecsact_system_execution_context* context, - ecsact_component_like_id comp_id + ecsact_component_like_id comp_id, + ... ) { assert(context != nullptr); - return context->remove(comp_id); + std::va_list indexed_fields; + va_start(indexed_fields, comp_id); + auto assoc_fields_hash = get_assoc_fields_hash( + ecsact_id_cast(comp_id), + indexed_fields + ); + va_end(indexed_fields); + return context->remove(comp_id, assoc_fields_hash); } void ecsact_system_execution_context_get( ecsact_system_execution_context* context, ecsact_component_like_id comp_id, - void* out_component_data + void* out_component_data, + ... ) { assert(context != nullptr); - return context->get(comp_id, out_component_data); + std::va_list indexed_fields; + va_start(indexed_fields, out_component_data); + auto assoc_fields_hash = get_assoc_fields_hash( + ecsact_id_cast(comp_id), + indexed_fields + ); + va_end(indexed_fields); + return context->get(comp_id, out_component_data, assoc_fields_hash); } void ecsact_system_execution_context_update( ecsact_system_execution_context* context, ecsact_component_like_id comp_id, - const void* component_data + const void* component_data, + ... ) { assert(context != nullptr); - return context->update(comp_id, component_data); + std::va_list indexed_fields; + va_start(indexed_fields, component_data); + auto assoc_fields_hash = get_assoc_fields_hash( + ecsact_id_cast(comp_id), + indexed_fields + ); + va_end(indexed_fields); + return context->update(comp_id, component_data, assoc_fields_hash); } bool ecsact_system_execution_context_has( ecsact_system_execution_context* context, - ecsact_component_like_id comp_id + ecsact_component_like_id comp_id, + ... ) { assert(context != nullptr); - return context->has(comp_id); + std::va_list indexed_fields; + va_start(indexed_fields, comp_id); + auto assoc_fields_hash = get_assoc_fields_hash( + ecsact_id_cast(comp_id), + indexed_fields + ); + va_end(indexed_fields); + return context->has(comp_id, assoc_fields_hash); } void ecsact_system_execution_context_action( @@ -123,3 +164,7 @@ const ecsact_system_execution_context* ecsact_system_execution_context_parent( assert(context != nullptr); return context->parent_ctx; } + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif diff --git a/runtime/hash.cc b/runtime/hash.cc index 9b1b177..75a3a46 100644 --- a/runtime/hash.cc +++ b/runtime/hash.cc @@ -1,10 +1,50 @@ #include "ecsact/entt/detail/hash.hh" #include "xxhash.h" -auto ecsact::entt::detail::bytes_hash( // +auto ecsact::entt::detail::bytes_hash32( // + std::byte* data, + int data_length +) -> std::uint32_t { + if(data_length == sizeof(std::uint32_t)) { + auto bytes_as_u32 = std::uint32_t{}; + std::memcpy(&bytes_as_u32, data, sizeof(std::uint32_t)); + return bytes_as_u32; + } else if(data_length == sizeof(std::uint16_t)) { + auto bytes_as_u16 = std::uint16_t{}; + std::memcpy(&bytes_as_u16, data, sizeof(std::uint16_t)); + return bytes_as_u16; + } else if(data_length == 1) { + return static_cast(data[0]); + } else if(data_length == 0) { + return 0; + } + + auto hash = XXH32(data, data_length, 1); + return hash; +} + +auto ecsact::entt::detail::bytes_hash64( // std::byte* data, int data_length ) -> std::uint64_t { + if(data_length == sizeof(std::uint64_t)) { + auto bytes_as_u64 = std::uint64_t{}; + std::memcpy(&bytes_as_u64, data, sizeof(std::uint64_t)); + return bytes_as_u64; + } else if(data_length == sizeof(std::uint32_t)) { + auto bytes_as_u32 = std::uint32_t{}; + std::memcpy(&bytes_as_u32, data, sizeof(std::uint32_t)); + return bytes_as_u32; + } else if(data_length == sizeof(std::uint16_t)) { + auto bytes_as_u16 = std::uint16_t{}; + std::memcpy(&bytes_as_u16, data, sizeof(std::uint16_t)); + return bytes_as_u16; + } else if(data_length == 1) { + return static_cast(data[0]); + } else if(data_length == 0) { + return 0; + } + XXH64_hash_t hash = XXH3_64bits(data, data_length); return hash; } diff --git a/runtime/index.bzl b/runtime/index.bzl index 238b050..648fe05 100644 --- a/runtime/index.bzl +++ b/runtime/index.bzl @@ -28,7 +28,6 @@ def ecsact_entt_runtime(name, srcs = [], deps = [], system_impls = [], tags = [] "@ecsact_lang_cpp//cpp_header_codegen", "@ecsact_lang_cpp//cpp_systems_header_codegen", "@ecsact_lang_cpp//systems_header_codegen", - "@ecsact_lang_cpp//cpp_meta_header_codegen", ], output_directory = "_%s__public_hdrs" % name, **kwargs @@ -42,7 +41,6 @@ def ecsact_entt_runtime(name, srcs = [], deps = [], system_impls = [], tags = [] strip_include_prefix = "_%s__public_hdrs" % name, deps = [ "@ecsact_lang_cpp//:execution_context", - "@ecsact_lang_cpp//:type_info", ], **kwargs ) diff --git a/test/MODULE.bazel b/test/MODULE.bazel index 5c51c23..87345cf 100644 --- a/test/MODULE.bazel +++ b/test/MODULE.bazel @@ -4,12 +4,12 @@ bazel_dep(name = "rules_cc", version = "0.0.9") bazel_dep(name = "bazel_skylib", version = "1.5.0") bazel_dep(name = "googletest", version = "1.14.0") bazel_dep(name = "rules_ecsact", version = "0.5.2") -bazel_dep(name = "ecsact_lang_cpp", version = "0.4.1") -bazel_dep(name = "ecsact_runtime", version = "0.6.1") +bazel_dep(name = "ecsact_lang_cpp", version = "0.4.3") +bazel_dep(name = "ecsact_runtime", version = "0.6.5") 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.9") +bazel_dep(name = "ecsact_cli", version = "0.3.11") bazel_dep(name = "boost.process", version = "1.83.0.bzl.2") bazel_dep(name = "toolchains_llvm", version = "1.0.0", dev_dependency = True) @@ -20,11 +20,14 @@ git_override( remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", ) -# TODO: https://github.com/bazelbuild/bazel-central-registry/pull/1916 -git_override( - module_name = "libarchive", - commit = "7c331f92acea5243c195cdc6fb46ecfa11ce1ce2", - remote = "https://github.com/zaucy/libarchive.git", +local_path_override( + module_name = "ecsact_runtime", + path = "../../ecsact_runtime", +) + +local_path_override( + module_name = "ecsact_lang_cpp", + path = "../../ecsact_lang_cpp", ) bazel_dep(name = "ecsact_rt_entt") diff --git a/test/assoc/BUILD.bazel b/test/assoc/BUILD.bazel new file mode 100644 index 0000000..1f3d83b --- /dev/null +++ b/test/assoc/BUILD.bazel @@ -0,0 +1,36 @@ +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 = ["assoc_test.ecsact"], + output_directory = "generated", + plugins = [ + "@ecsact_lang_cpp//cpp_systems_source_codegen", + ], +) + +ecsact_entt_runtime( + name = "runtime", + srcs = ["assoc_test.ecsact"], + ECSACT_ENTT_RUNTIME_PACKAGE = "::assoc_test::package", + ECSACT_ENTT_RUNTIME_USER_HEADER = "assoc_test.ecsact.meta.hh", + system_impls = ["dynamic"], +) + +cc_test( + name = "test", + srcs = [ + "assoc_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/assoc/assoc_test.cc b/test/assoc/assoc_test.cc new file mode 100644 index 0000000..6b639bd --- /dev/null +++ b/test/assoc/assoc_test.cc @@ -0,0 +1,149 @@ +#include "gtest/gtest.h" + +#include +#include "entt/entt.hpp" +#include "ecsact/runtime/core.hh" +#include "ecsact/runtime/dynamic.h" +#include "assoc_test.ecsact.hh" +#include "assoc_test.ecsact.systems.hh" + +using namespace assoc_test; + +#define SET_SYSTEM_IMPL(SystemName) \ + ASSERT_TRUE(ecsact_set_system_execution_impl( \ + ecsact_id_cast(SystemName::id), \ + &assoc_test__##SystemName \ + )) +#define CLEAR_SYSTEM_IMPL(SystemName) \ + ASSERT_TRUE(ecsact_set_system_execution_impl( \ + ecsact_id_cast(SystemName::id), \ + nullptr \ + )) + +static std::atomic_int FieldAssocSystem_exec_count = 0; + +auto FieldAssocSystem::impl(context& ctx) -> void { + FieldAssocSystem_exec_count += 1; +} + +TEST(Assoc, EnttSanityChecks) { + using namespace entt::literals; + + struct A { + int32_t n; + }; + + auto reg = entt::registry{}; + auto entity = reg.create(); + + auto other_storage_id = entt::id_type{1232131231}; + + reg.storage().emplace(entity, A{10}); + ASSERT_TRUE(reg.storage().contains(entity)); + ASSERT_FALSE(reg.storage(other_storage_id).contains(entity)); + + reg.storage(other_storage_id).emplace(entity, A{20}); + ASSERT_TRUE(reg.storage().contains(entity)); + ASSERT_TRUE(reg.storage(other_storage_id).contains(entity)); + + ASSERT_EQ(reg.storage().get(entity).n, 10); + ASSERT_EQ(reg.storage(other_storage_id).get(entity).n, 20); +} + +TEST(AssocCore, EntityAssoc) { + auto reg = ecsact::core::registry{"EntityAssoc"}; + auto entity1 = reg.create_entity(); + auto entity2 = reg.create_entity(); + + ASSERT_FALSE(reg.has_component(entity1, entity2)); + ASSERT_FALSE(reg.has_component(entity1, entity1)); + + ASSERT_EQ( + reg.add_component(entity1, EntityAssoc{entity2, 10}), + ECSACT_ADD_OK + ); + ASSERT_TRUE(reg.has_component(entity1, entity2)); + EXPECT_FALSE(reg.has_component(entity1, entity1)); + ASSERT_EQ(reg.get_component(entity1, entity2).n, 10); + + ASSERT_EQ( + reg.update_component(entity1, EntityAssoc{entity1, 12}, entity2), + ECSACT_UPDATE_OK + ); + EXPECT_TRUE(reg.has_component(entity1, entity1)); + EXPECT_FALSE(reg.has_component(entity1, entity2)); + + ASSERT_EQ( + reg.add_component(entity1, EntityAssoc{entity2, 18}), + ECSACT_ADD_OK + ); + ASSERT_TRUE(reg.has_component(entity1, entity1)); + ASSERT_TRUE(reg.has_component(entity1, entity2)); + ASSERT_EQ(reg.get_component(entity1, entity1).n, 12); + ASSERT_EQ(reg.get_component(entity1, entity2).n, 18); + + reg.remove_component(entity1, entity2); + ASSERT_FALSE(reg.has_component(entity1, entity2)); + ASSERT_TRUE(reg.has_component(entity1, entity1)); + ASSERT_EQ(reg.get_component(entity1, entity1).n, 12); + + reg.remove_component(entity1, entity1); + ASSERT_FALSE(reg.has_component(entity1, entity2)); + ASSERT_FALSE(reg.has_component(entity1, entity1)); +} + +TEST(AssocCore, FieldAssoc) { + auto reg = ecsact::core::registry{"FieldAssoc"}; + + auto entity1 = reg.create_entity(); + auto entity2 = reg.create_entity(); + + ASSERT_FALSE(reg.has_component(entity1, 10)); + ASSERT_FALSE(reg.has_component(entity1, 11)); + + ASSERT_EQ(reg.add_component(entity1, FieldAssoc{10, 44}), ECSACT_ADD_OK); + ASSERT_TRUE(reg.has_component(entity1, 10)); + ASSERT_FALSE(reg.has_component(entity1, 11)); + + ASSERT_EQ(reg.add_component(entity1, FieldAssoc{11, 44}), ECSACT_ADD_OK); + ASSERT_TRUE(reg.has_component(entity1, 10)); + ASSERT_TRUE(reg.has_component(entity1, 11)); + + ASSERT_EQ( + reg.update_component(entity1, FieldAssoc{9, 44}, 10), + ECSACT_UPDATE_OK + ); + ASSERT_FALSE(reg.has_component(entity1, 10)); + ASSERT_TRUE(reg.has_component(entity1, 9)); + ASSERT_TRUE(reg.has_component(entity1, 11)); + + reg.remove_component(entity1, 9); + ASSERT_FALSE(reg.has_component(entity1, 9)); + ASSERT_TRUE(reg.has_component(entity1, 11)); +} + +TEST(AssocCore, FieldAssocExecutionCount) { + SET_SYSTEM_IMPL(FieldAssocSystem); + + auto reg = ecsact::core::registry{"FieldAssocExecutionCount"}; + + auto entity1 = reg.create_entity(); + auto entity2 = reg.create_entity(); + + reg.add_component(entity1, FieldAssoc{10, 22}); + reg.add_component(entity1, FieldAssoc{11, 30}); + reg.add_component(entity1, FieldAssoc{16, 48}); + + reg.execute_systems(); + EXPECT_EQ(FieldAssocSystem_exec_count, 0); + FieldAssocSystem_exec_count = 0; + + reg.add_component(entity2, FieldAssoc{10, 55}); + reg.add_component(entity2, A{57}); + + reg.execute_systems(); + EXPECT_EQ(FieldAssocSystem_exec_count, 1); + FieldAssocSystem_exec_count = 0; + + CLEAR_SYSTEM_IMPL(FieldAssocSystem); +} diff --git a/test/assoc/assoc_test.ecsact b/test/assoc/assoc_test.ecsact new file mode 100644 index 0000000..24ff1c8 --- /dev/null +++ b/test/assoc/assoc_test.ecsact @@ -0,0 +1,14 @@ +main package assoc_test; + +component EntityAssoc { entity e; i32 n; } +component Unique { i32 my_id; } +component FieldAssoc { Unique.my_id unique_id; i32 num; } + +component A { i32 a; } +component B; + +system FieldAssocSystem { + readonly FieldAssoc; + adds B; +} + diff --git a/test/runtime_test.cc b/test/runtime_test.cc index 13ee741..6d013c5 100644 --- a/test/runtime_test.cc +++ b/test/runtime_test.cc @@ -35,7 +35,7 @@ void runtime_test::SimpleSystem::impl(context& ctx) { void runtime_test::OtherEntitySystem::impl(context& ctx) { auto comp = ctx.get(); - auto other = ctx._ctx.other(comp.target); + auto other = ctx.other<0>(); auto other_comp = other.get(); comp.num += -other_comp.a; @@ -93,7 +93,7 @@ void runtime_test::AssocTestAction::impl(context& ctx) { void runtime_test::AttackDamage::impl(context& ctx) { // auto attacking = ctx.get(); - // auto target_ctx = ctx._ctx.other(attacking.target); + // auto target_ctx = ctx._ctx.other(); // auto target_health = target_ctx.get(); // target_health.value -= 1.f; // target_ctx.update(target_health); @@ -101,7 +101,7 @@ void runtime_test::AttackDamage::impl(context& ctx) { void runtime_test::AttackDamageWeakened::impl(context& ctx) { // auto attacking = ctx.get(); - // auto target_ctx = ctx._ctx.other(attacking.target); + // auto target_ctx = ctx._ctx.other(); // auto target_health = target_ctx.get(); // auto target_weakened = target_ctx.get(); // target_health.value -= 1.f * target_weakened.value; @@ -157,8 +157,8 @@ void runtime_test::AddAssocTest::impl(context& ctx) { auto other_entity = ctx.get(); // Get Target other context from OtherEntityComponent - auto target_ctx = ctx._ctx.other(other_entity.target); - assert(target_ctx._ctx != nullptr); + auto target_ctx = ctx.other(); + assert(target_ctx._ctx._ctx != nullptr); target_ctx.add(AddAssocTestComponent{.num = 10}); } @@ -169,7 +169,7 @@ void runtime_test::RemoveAssocTest::impl(context& ctx) { auto other_entity = ctx.get(); // Get Target other context from OtherEntityComponent - auto target_ctx = ctx._ctx.other(other_entity.target); + auto target_ctx = ctx.other(); target_ctx.remove(); } @@ -694,9 +694,9 @@ TEST(Core, AssociationEntityCorrectness) { ecsact_id_cast(AttackDamageWeakened::id), [](ecsact_system_execution_context* cctx) { ++attack_damage_weakened_exec_count; - ecsact::execution_context ctx{cctx}; + AttackDamageWeakened::context ctx{cctx}; ASSERT_TRUE(attacker_entities.contains(ctx.entity())); - auto target_ctx = ctx.other(ctx.get().target); + auto target_ctx = ctx.other(); ASSERT_TRUE(weakened_target_entities.contains(target_ctx.entity())); } ); @@ -705,11 +705,11 @@ TEST(Core, AssociationEntityCorrectness) { ecsact_id_cast(AttackDamage::id), [](ecsact_system_execution_context* cctx) { ++attack_damage_exec_count; - ecsact::execution_context ctx{cctx}; + AttackDamage::context ctx{cctx}; ASSERT_TRUE(attacker_entities.contains(ctx.entity())); // Sanity check - no exception - ctx.other(ctx.get().target); + ctx.other(); } ); diff --git a/test/spawn.cc b/test/spawn.cc index be435f1..6f2703b 100644 --- a/test/spawn.cc +++ b/test/spawn.cc @@ -1,6 +1,7 @@ #include "spawn.hh" #include +#include namespace bp = boost::process;