diff --git a/include/flecs.h b/include/flecs.h index 9ebf225a91..0038a2d176 100644 --- a/include/flecs.h +++ b/include/flecs.h @@ -432,6 +432,16 @@ typedef struct ecs_type_hooks_t ecs_type_hooks_t; * alignment and type hooks. */ typedef struct ecs_type_info_t ecs_type_info_t; +/** Cached Type information. + * Contains information about a component type, such as its id, size and alignment */ +typedef struct ecs_cached_component_info_t ecs_cached_component_info_t; + +/** An index to a cached component id information. + * Can be used to map a typed component from object-oriented language + * fast to a dynamically-per-world generated component id. + * Components are resolved by name lookup and subsequently cached. */ +typedef int32_t ecs_component_cache_index_t; + /** Information about an entity, like its table and row. */ typedef struct ecs_record_t ecs_record_t; @@ -864,6 +874,16 @@ struct ecs_type_info_t { const char *name; /**< Type name. */ }; +/** Type that contains cache component information + * + * \ingroup components + */ +struct ecs_cached_component_info_t { + ecs_entity_t component; /**< Handle to component */ + ecs_size_t size; /**< Size of type */ + ecs_size_t alignment; /**< Alignment of type */ +}; + #include "flecs/private/api_types.h" /* Supporting API types */ #include "flecs/private/api_support.h" /* Supporting API functions */ #include "flecs/private/vec.h" /* Vector */ @@ -3690,6 +3710,38 @@ const ecs_type_hooks_t* ecs_get_hooks_id( ecs_world_t *world, ecs_entity_t id); +/** Get the cached information for a specific component cache index. + * + * @param world The world. + * @param component_cache_index The component cache index to lookup. + * @return The cached component info for the specific component, always returns a present entry. + */ +FLECS_API +ecs_cached_component_info_t* ecs_get_or_create_cached_component_info( + ecs_world_t* world, + ecs_component_cache_index_t component_cache_index); + +/** Get the valid cached information for a specific component cache index. + * + * @param world The world. + * @param component_cache_index The component cache index to lookup. + * @return The valid cached component info for the specific component or NULL if invalid. + */ +FLECS_API +const ecs_cached_component_info_t* ecs_lookup_cached_component_info( + const ecs_world_t* world, + ecs_component_cache_index_t component_cache_index); + + +/** Test if the cached component info is valid (set) + * + * @param component_info The component cache index to lookup. + * @return True if the info is valid. + */ +FLECS_API +bool ecs_is_cached_component_info_valid( + const ecs_cached_component_info_t* component_info); + /** @} */ /** diff --git a/include/flecs/addons/cpp/component.hpp b/include/flecs/addons/cpp/component.hpp index 2cec47ed10..3590c523f8 100644 --- a/include/flecs/addons/cpp/component.hpp +++ b/include/flecs/addons/cpp/component.hpp @@ -125,61 +125,20 @@ void register_lifecycle_actions( // will register it as a component, and verify whether the input is consistent. template struct cpp_type_impl { - // Initialize component identifier - static void init( - entity_t entity, - bool allow_tag = true) - { - if (s_reset_count != ecs_cpp_reset_count_get()) { - reset(); - } - - // If an identifier was already set, check for consistency - if (s_id) { - ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID, - type_name()); - ecs_assert(allow_tag == s_allow_tag, ECS_INVALID_PARAMETER, NULL); - - // Component was already registered and data is consistent with new - // identifier, so nothing else to be done. - return; - } - - // Component wasn't registered yet, set the values. Register component - // name as the fully qualified flecs path. - s_id = entity; - s_allow_tag = allow_tag; - s_size = sizeof(T); - s_alignment = alignof(T); - if (is_empty::value && allow_tag) { - s_size = 0; - s_alignment = 0; - } - - s_reset_count = ecs_cpp_reset_count_get(); - } - // Obtain a component identifier for explicit component registration. - static entity_t id_explicit(world_t *world = nullptr, + static entity_t id_explicit(world_t *world, const char *name = nullptr, bool allow_tag = true, flecs::id_t id = 0, bool is_component = true, bool *existing = nullptr) { - if (!s_id) { - // If no world was provided the component cannot be registered - ecs_assert(world != nullptr, ECS_COMPONENT_NOT_REGISTERED, name); - } else { - ecs_assert(!id || s_id == id, ECS_INCONSISTENT_COMPONENT_ID, NULL); - } - - // If no id has been registered yet for the component (indicating the - // component has not yet been registered, or the component is used - // across more than one binary), or if the id does not exists in the - // world (indicating a multi-world application), register it. */ - if (!s_id || (world && !ecs_exists(world, s_id))) { - init(s_id ? s_id : id, allow_tag); - - ecs_assert(!id || s_id == id, ECS_INTERNAL_ERROR, NULL); + ecs_assert(world != nullptr, ECS_INTERNAL_ERROR, name); + if (const ecs_cached_component_info_t* info = ecs_lookup_cached_component_info(world, index)) { + return info->component; + } else { + // If no id has been registered yet for the component (indicating the + // component has not yet been registered), or if the id does not + // exists in the world (indicating a multi-world application), + // register it. */ const char *symbol = nullptr; if (id) { symbol = ecs_get_symbol(world, id); @@ -188,23 +147,37 @@ struct cpp_type_impl { symbol = symbol_name(); } - entity_t entity = ecs_cpp_component_register_explicit( - world, s_id, id, name, type_name(), symbol, - s_size, s_alignment, is_component, existing); + const bool is_tag = is_empty::value && allow_tag; + + const size_t component_size = is_tag ? 0U : size(); + const size_t component_alignment = is_tag ? 0U : alignment(); + + const entity_t entity = ecs_cpp_component_register_explicit( + world, 0, id, name, type_name(), symbol, + component_size, component_alignment, is_component, existing); + + // Component wasn't registered yet, set the values. Register component + // name as the fully qualified flecs path. + ecs_cached_component_info_t* inserted = + ecs_get_or_create_cached_component_info(world, index); - s_id = entity; + ecs_assert(!!inserted, ECS_INTERNAL_ERROR, NULL); + ecs_assert(!ecs_is_cached_component_info_valid(inserted), ECS_INTERNAL_ERROR, + NULL); + + inserted->component = entity; + inserted->size = static_cast(component_size); + inserted->alignment = static_cast(component_alignment); + + ecs_assert(ecs_is_cached_component_info_valid(inserted), ECS_INTERNAL_ERROR, NULL); // If component is enum type, register constants #if FLECS_CPP_ENUM_REFLECTION_SUPPORT _::init_enum(world, entity); #endif - } - // By now the identifier must be valid and known with the world. - ecs_assert(s_id != 0 && ecs_exists(world, s_id), - ECS_INTERNAL_ERROR, NULL); - - return s_id; + return entity; + } } // Obtain a component identifier for implicit component registration. This @@ -213,29 +186,28 @@ struct cpp_type_impl { // Additionally, implicit registration temporarily resets the scope & with // state of the world, so that the component is not implicitly created with // the scope/with of the code it happens to be first used by. - static id_t id(world_t *world = nullptr, const char *name = nullptr, + static id_t id(world_t *world, const char *name = nullptr, bool allow_tag = true) { - // If no id has been registered yet, do it now. - if (!registered(world)) { - ecs_entity_t prev_scope = 0; - ecs_id_t prev_with = 0; - - if (world) { - prev_scope = ecs_set_scope(world, 0); - prev_with = ecs_set_with(world, 0); - } + ecs_assert(world != nullptr, ECS_INTERNAL_ERROR, name); + + if (const ecs_cached_component_info_t* info = ecs_lookup_cached_component_info(world, index)) { + return info->component; + } else { + // If no id has been registered yet, do it now. + const ecs_entity_t prev_scope = ecs_set_scope(world, 0); + const ecs_id_t prev_with = ecs_set_with(world, 0); // This will register a component id, but will not register // lifecycle callbacks. bool existing; - id_explicit(world, name, allow_tag, 0, true, &existing); + const entity_t id = id_explicit(world, name, allow_tag, 0, true, &existing); // Register lifecycle callbacks, but only if the component has a // size. Components that don't have a size are tags, and tags don't // require construction/destruction/copy/move's. */ if (size() && !existing) { - register_lifecycle_actions(world, s_id); + register_lifecycle_actions(world, id); } if (prev_with) { @@ -244,62 +216,41 @@ struct cpp_type_impl { if (prev_scope) { ecs_set_scope(world, prev_scope); } - } - - // By now we should have a valid identifier - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); - return s_id; + return id; + } } - // Return the size of a component. - static size_t size() { - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); - return s_size; + /// Looks the assigned component up in the provided world. + /// It can happen that the component has not been initialized yet. + static entity_t lookup(const world_t* world) { + const ecs_cached_component_info_t* info = ecs_lookup_cached_component_info(world, index); + return info ? info->component : 0; } - // Return the alignment of a component. - static size_t alignment() { - ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); - return s_alignment; + // Was the component already registered. + static bool registered(const world_t* world) { + return !!lookup(world); } - // Was the component already registered. - static bool registered(flecs::world_t *world) { - if (s_reset_count != ecs_cpp_reset_count_get()) { - reset(); - } - if (s_id == 0) { - return false; - } - if (world && !ecs_exists(world, s_id)) { - return false; - } - return true; + // Return the size of this component. + static size_t size() { + return sizeof(T); } - // This function is only used to test cross-translation unit features. No - // code other than test cases should invoke this function. - static void reset() { - s_id = 0; - s_size = 0; - s_alignment = 0; - s_allow_tag = true; + // Return the alignment of this component. + static size_t alignment() { + return alignof(T); } - static entity_t s_id; - static size_t s_size; - static size_t s_alignment; - static bool s_allow_tag; - static int32_t s_reset_count; + // Acquire a per instance incremental index for a world-local component id cache. + static ecs_component_cache_index_t index; }; // Global templated variables that hold component identifier and other info -template entity_t cpp_type_impl::s_id; -template size_t cpp_type_impl::s_size; -template size_t cpp_type_impl::s_alignment; -template bool cpp_type_impl::s_allow_tag( true ); -template int32_t cpp_type_impl::s_reset_count; +template +ecs_component_cache_index_t cpp_type_impl::index{ + ecs_cpp_component_id_storage_add()}; // Front facing class for implicitly registering a component & obtaining // static component data @@ -375,10 +326,9 @@ struct component : untyped_component { implicit_name = true; } - if (_::cpp_type::registered(world)) { - /* Obtain component id. Because the component is already registered, - * this operation does nothing besides returning the existing id */ - id = _::cpp_type::id_explicit(world, name, allow_tag, id); + /* Obtain a registered component id. */ + if (const entity_t registered = _::cpp_type::lookup(world)) { + id = registered; ecs_cpp_component_validate(world, id, n, _::symbol_name(), _::cpp_type::size(), @@ -504,17 +454,6 @@ struct component : untyped_component { } }; -/** Get id currently assigned to component. If no world has registered the - * component yet, this operation will return 0. */ -template -flecs::entity_t type_id() { - if (_::cpp_type::s_reset_count == ecs_cpp_reset_count_get()) { - return _::cpp_type::s_id; - } else { - return 0; - } -} - /** Reset static component ids. * When components are registered their component ids are stored in a static * type specific variable. This stored id is passed into component registration @@ -537,9 +476,9 @@ flecs::entity_t type_id() { * * \ingroup cpp_components */ -inline void reset() { - ecs_cpp_reset_count_inc(); -} +ECS_DEPRECATED("ecs_cpp_reset_count_inc was deprecated, world-local component " + "ids made it obsolete") +inline void reset() {} } diff --git a/include/flecs/addons/cpp/mixins/meta/impl.hpp b/include/flecs/addons/cpp/mixins/meta/impl.hpp index 31cb533953..d5c2b60938 100644 --- a/include/flecs/addons/cpp/mixins/meta/impl.hpp +++ b/include/flecs/addons/cpp/mixins/meta/impl.hpp @@ -64,18 +64,16 @@ inline void init(flecs::world& world) { // specific types. if (!flecs::is_same() && !flecs::is_same()) { - flecs::_::cpp_type::init(flecs::Iptr, true); - ecs_assert(flecs::type_id() == flecs::Iptr, - ECS_INTERNAL_ERROR, NULL); + const entity_t id = flecs::_::cpp_type::id_explicit(world, nullptr, flecs::Iptr, true); + ecs_assert(id == flecs::Iptr, ECS_INTERNAL_ERROR, NULL); // Remove symbol to prevent validation errors, as it doesn't match with // the typename ecs_remove_pair(world, flecs::Iptr, ecs_id(EcsIdentifier), EcsSymbol); } if (!flecs::is_same() && !flecs::is_same()) { - flecs::_::cpp_type::init(flecs::Uptr, true); - ecs_assert(flecs::type_id() == flecs::Uptr, - ECS_INTERNAL_ERROR, NULL); + const entity_t id = flecs::_::cpp_type::id_explicit(world, nullptr, flecs::Uptr, true); + ecs_assert(id == flecs::Uptr, ECS_INTERNAL_ERROR, NULL); // Remove symbol to prevent validation errors, as it doesn't match with // the typename ecs_remove_pair(world, flecs::Uptr, ecs_id(EcsIdentifier), EcsSymbol); diff --git a/include/flecs/addons/cpp/mixins/module/impl.hpp b/include/flecs/addons/cpp/mixins/module/impl.hpp index d99c2e7d29..ca22d280ca 100644 --- a/include/flecs/addons/cpp/mixins/module/impl.hpp +++ b/include/flecs/addons/cpp/mixins/module/impl.hpp @@ -43,12 +43,8 @@ flecs::entity import(world& world) { if (!_::cpp_type::registered(world)) { - /* Module is registered with world, initialize static data */ - if (m) { - _::cpp_type::init(m, false); - /* Module is not yet registered, register it now */ - } else { + if (!m) { m = _::do_import(world, symbol); } diff --git a/include/flecs/addons/flecs_cpp.h b/include/flecs/addons/flecs_cpp.h index 2ee2642f79..996a0194b2 100644 --- a/include/flecs/addons/flecs_cpp.h +++ b/include/flecs/addons/flecs_cpp.h @@ -112,11 +112,16 @@ ecs_entity_t ecs_cpp_enum_constant_register( const char *name, int value); -FLECS_API -int32_t ecs_cpp_reset_count_get(void); +ECS_DEPRECATED("ecs_cpp_reset_count_get was deprecated, world-local component " + "ids made it obsolete") +FLECS_API int32_t ecs_cpp_reset_count_get(void); + +ECS_DEPRECATED("ecs_cpp_reset_count_inc was deprecated, world-local component " + "ids made it obsolete") +FLECS_API int32_t ecs_cpp_reset_count_inc(void); FLECS_API -int32_t ecs_cpp_reset_count_inc(void); +ecs_size_t ecs_cpp_component_id_storage_add(void); #ifdef FLECS_META FLECS_API diff --git a/src/addons/flecs_cpp.c b/src/addons/flecs_cpp.c index c62b7bb5e0..b281307412 100644 --- a/src/addons/flecs_cpp.c +++ b/src/addons/flecs_cpp.c @@ -482,14 +482,18 @@ ecs_entity_t ecs_cpp_enum_constant_register( return id; } -static int32_t flecs_reset_count = 0; - int32_t ecs_cpp_reset_count_get(void) { - return flecs_reset_count; + return 0; // Deprecated } int32_t ecs_cpp_reset_count_inc(void) { - return ++flecs_reset_count; + return 0; // Deprecated +} + +static ecs_size_t flecs_component_storage_count = 0; + +ecs_size_t ecs_cpp_component_id_storage_add(void) { + return flecs_component_storage_count++; } #ifdef FLECS_META diff --git a/src/private_types.h b/src/private_types.h index cd55322db2..fe4cb05164 100644 --- a/src/private_types.h +++ b/src/private_types.h @@ -545,6 +545,9 @@ struct ecs_world_t { ecs_ctx_free_t binding_ctx_free; /**< Callback to free binding_ctx */ ecs_vec_t fini_actions; /* Callbacks to execute when world exits */ + + /* -- Caches -- */ + ecs_vec_t component_id_cache; /* World local component id cache */ }; #endif diff --git a/src/world.c b/src/world.c index 9a3751e4ae..3e09f84bee 100644 --- a/src/world.c +++ b/src/world.c @@ -897,6 +897,8 @@ ecs_world_t *ecs_mini(void) { flecs_name_index_init(&world->symbols, a); ecs_vec_init_t(a, &world->fini_actions, ecs_action_elem_t, 0); + ecs_vec_init_t(a, &world->component_id_cache, ecs_cached_component_info_t, 0); + world->info.time_scale = 1.0; if (ecs_os_has_time()) { ecs_os_get_time(&world->world_start_time); @@ -1240,6 +1242,57 @@ const ecs_type_hooks_t* ecs_get_hooks_id( return NULL; } +ecs_cached_component_info_t* ecs_get_or_create_cached_component_info( + ecs_world_t* world, + ecs_component_cache_index_t component_cache_index) { + + ecs_poly_assert(world, ecs_world_t); + + const int32_t old_count = ecs_vec_count(&world->component_id_cache); + if (component_cache_index >= old_count) { + const int32_t new_count = component_cache_index + 1; + + ecs_vec_set_count_t(&world->allocator, &world->component_id_cache, + ecs_cached_component_info_t, new_count); + + ecs_cached_component_info_t *added = + ecs_vec_get_t(&world->component_id_cache, + ecs_cached_component_info_t, old_count); + + /* Array may have holes, so initialize with 0 */ + ecs_os_memset_n(added, 0, ecs_cached_component_info_t, + new_count - old_count); + } + + return ecs_vec_get_t(&world->component_id_cache, + ecs_cached_component_info_t, component_cache_index); +} + +const ecs_cached_component_info_t* ecs_lookup_cached_component_info( + const ecs_world_t* world, + ecs_component_cache_index_t component_cache_index) { + + ecs_poly_assert(world, ecs_world_t); + + if (component_cache_index >= ecs_vec_count(&world->component_id_cache)) { + return NULL; + } + + const ecs_cached_component_info_t *info = + ecs_vec_get_t(&world->component_id_cache, ecs_cached_component_info_t, + component_cache_index); + + if (!ecs_is_cached_component_info_valid(info)) { + return NULL; + } + + return info; +} + +bool ecs_is_cached_component_info_valid(const ecs_cached_component_info_t* component_info) { + return component_info->component != 0; +} + void ecs_atfini( ecs_world_t *world, ecs_fini_action_t action, @@ -1359,6 +1412,8 @@ int ecs_fini( world->flags |= EcsWorldFini; + ecs_vec_fini_t(&world->allocator, &world->component_id_cache, ecs_cached_component_info_t); + /* Run fini actions (simple callbacks ran when world is deleted) before * destroying the storage */ ecs_dbg_1("#[bold]run fini actions");