Skip to content

Commit db46049

Browse files
committed
Implement a world-local component id caching API and make use of it in cpp components
* Fixes potential conflicting component id issues when initializing different worlds with a different order. * Closes SanderMertens#1032
1 parent 3b03007 commit db46049

File tree

14 files changed

+274
-302
lines changed

14 files changed

+274
-302
lines changed

include/flecs.h

+52
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,16 @@ typedef struct ecs_type_hooks_t ecs_type_hooks_t;
432432
* alignment and type hooks. */
433433
typedef struct ecs_type_info_t ecs_type_info_t;
434434

435+
/** Cached Type information.
436+
* Contains information about a component type, such as its id, size and alignment */
437+
typedef struct ecs_cached_component_info_t ecs_cached_component_info_t;
438+
439+
/** An index to a cached component id information.
440+
* Can be used to map a typed component from object-oriented language
441+
* fast to a dynamically-per-world generated component id.
442+
* Components are resolved by name lookup and subsequently cached. */
443+
typedef int32_t ecs_component_cache_index_t;
444+
435445
/** Information about an entity, like its table and row. */
436446
typedef struct ecs_record_t ecs_record_t;
437447

@@ -864,6 +874,16 @@ struct ecs_type_info_t {
864874
const char *name; /**< Type name. */
865875
};
866876

877+
/** Type that contains cache component information
878+
*
879+
* \ingroup components
880+
*/
881+
struct ecs_cached_component_info_t {
882+
ecs_entity_t component; /**< Handle to component */
883+
ecs_size_t size; /**< Size of type */
884+
ecs_size_t alignment; /**< Alignment of type */
885+
};
886+
867887
#include "flecs/private/api_types.h" /* Supporting API types */
868888
#include "flecs/private/api_support.h" /* Supporting API functions */
869889
#include "flecs/private/vec.h" /* Vector */
@@ -3690,6 +3710,38 @@ const ecs_type_hooks_t* ecs_get_hooks_id(
36903710
ecs_world_t *world,
36913711
ecs_entity_t id);
36923712

3713+
/** Get the cached information for a specific component cache index.
3714+
*
3715+
* @param world The world.
3716+
* @param component_cache_index The component cache index to lookup.
3717+
* @return The cached component info for the specific component, always returns a present entry.
3718+
*/
3719+
FLECS_API
3720+
ecs_cached_component_info_t* ecs_get_or_create_cached_component_info(
3721+
ecs_world_t* world,
3722+
ecs_component_cache_index_t component_cache_index);
3723+
3724+
/** Get the valid cached information for a specific component cache index.
3725+
*
3726+
* @param world The world.
3727+
* @param component_cache_index The component cache index to lookup.
3728+
* @return The valid cached component info for the specific component or NULL if invalid.
3729+
*/
3730+
FLECS_API
3731+
const ecs_cached_component_info_t* ecs_lookup_cached_component_info(
3732+
const ecs_world_t* world,
3733+
ecs_component_cache_index_t component_cache_index);
3734+
3735+
3736+
/** Test if the cached component info is valid (set)
3737+
*
3738+
* @param component_info The component cache index to lookup.
3739+
* @return True if the info is valid.
3740+
*/
3741+
FLECS_API
3742+
bool ecs_is_cached_component_info_valid(
3743+
const ecs_cached_component_info_t* component_info);
3744+
36933745
/** @} */
36943746

36953747
/**

include/flecs/addons/cpp/component.hpp

+78-132
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ void register_lifecycle_actions(
113113
ecs_set_hooks_id( world, component, &cl);
114114
}
115115

116+
// Instantiates a per-instance global component cache index
117+
struct cpp_type_component_cache_index {
118+
cpp_type_component_cache_index()
119+
: index(ecs_cpp_component_id_storage_add()) {}
120+
121+
ecs_component_cache_index_t const index;
122+
};
123+
116124
// Class that manages component ids across worlds & binaries.
117125
// The cpp_type class stores the component id for a C++ type in a static global
118126
// variable that is shared between worlds. Whenever a component is used this
@@ -125,61 +133,20 @@ void register_lifecycle_actions(
125133
// will register it as a component, and verify whether the input is consistent.
126134
template <typename T>
127135
struct cpp_type_impl {
128-
// Initialize component identifier
129-
static void init(
130-
entity_t entity,
131-
bool allow_tag = true)
132-
{
133-
if (s_reset_count != ecs_cpp_reset_count_get()) {
134-
reset();
135-
}
136-
137-
// If an identifier was already set, check for consistency
138-
if (s_id) {
139-
ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID,
140-
type_name<T>());
141-
ecs_assert(allow_tag == s_allow_tag, ECS_INVALID_PARAMETER, NULL);
142-
143-
// Component was already registered and data is consistent with new
144-
// identifier, so nothing else to be done.
145-
return;
146-
}
147-
148-
// Component wasn't registered yet, set the values. Register component
149-
// name as the fully qualified flecs path.
150-
s_id = entity;
151-
s_allow_tag = allow_tag;
152-
s_size = sizeof(T);
153-
s_alignment = alignof(T);
154-
if (is_empty<T>::value && allow_tag) {
155-
s_size = 0;
156-
s_alignment = 0;
157-
}
158-
159-
s_reset_count = ecs_cpp_reset_count_get();
160-
}
161-
162136
// Obtain a component identifier for explicit component registration.
163-
static entity_t id_explicit(world_t *world = nullptr,
137+
static entity_t id_explicit(world_t *world,
164138
const char *name = nullptr, bool allow_tag = true, flecs::id_t id = 0,
165139
bool is_component = true, bool *existing = nullptr)
166140
{
167-
if (!s_id) {
168-
// If no world was provided the component cannot be registered
169-
ecs_assert(world != nullptr, ECS_COMPONENT_NOT_REGISTERED, name);
170-
} else {
171-
ecs_assert(!id || s_id == id, ECS_INCONSISTENT_COMPONENT_ID, NULL);
172-
}
173-
174-
// If no id has been registered yet for the component (indicating the
175-
// component has not yet been registered, or the component is used
176-
// across more than one binary), or if the id does not exists in the
177-
// world (indicating a multi-world application), register it. */
178-
if (!s_id || (world && !ecs_exists(world, s_id))) {
179-
init(s_id ? s_id : id, allow_tag);
180-
181-
ecs_assert(!id || s_id == id, ECS_INTERNAL_ERROR, NULL);
141+
ecs_assert(world != nullptr, ECS_INTERNAL_ERROR, name);
182142

143+
if (const ecs_cached_component_info_t* info = ecs_lookup_cached_component_info(world, cached_component_index.index)) {
144+
return info->component;
145+
} else {
146+
// If no id has been registered yet for the component (indicating the
147+
// component has not yet been registered), or if the id does not
148+
// exists in the world (indicating a multi-world application),
149+
// register it. */
183150
const char *symbol = nullptr;
184151
if (id) {
185152
symbol = ecs_get_symbol(world, id);
@@ -188,23 +155,37 @@ struct cpp_type_impl {
188155
symbol = symbol_name<T>();
189156
}
190157

191-
entity_t entity = ecs_cpp_component_register_explicit(
192-
world, s_id, id, name, type_name<T>(), symbol,
193-
s_size, s_alignment, is_component, existing);
158+
const bool is_tag = is_empty<T>::value && allow_tag;
194159

195-
s_id = entity;
160+
const size_t component_size = is_tag ? 0U : size();
161+
const size_t component_alignment = is_tag ? 0U : alignment();
162+
163+
const entity_t entity = ecs_cpp_component_register_explicit(
164+
world, id, name, type_name<T>(), symbol,
165+
component_size, component_alignment, is_component, existing);
166+
167+
// Component wasn't registered yet, set the values. Register component
168+
// name as the fully qualified flecs path.
169+
ecs_cached_component_info_t* inserted =
170+
ecs_get_or_create_cached_component_info(world, cached_component_index.index);
171+
172+
ecs_assert(!!inserted, ECS_INTERNAL_ERROR, NULL);
173+
ecs_assert(!ecs_is_cached_component_info_valid(inserted), ECS_INTERNAL_ERROR,
174+
NULL);
175+
176+
inserted->component = entity;
177+
inserted->size = static_cast<ecs_size_t>(component_size);
178+
inserted->alignment = static_cast<ecs_size_t>(component_alignment);
179+
180+
ecs_assert(ecs_is_cached_component_info_valid(inserted), ECS_INTERNAL_ERROR, NULL);
196181

197182
// If component is enum type, register constants
198183
#if FLECS_CPP_ENUM_REFLECTION_SUPPORT
199184
_::init_enum<T>(world, entity);
200185
#endif
201-
}
202186

203-
// By now the identifier must be valid and known with the world.
204-
ecs_assert(s_id != 0 && ecs_exists(world, s_id),
205-
ECS_INTERNAL_ERROR, NULL);
206-
207-
return s_id;
187+
return entity;
188+
}
208189
}
209190

210191
// Obtain a component identifier for implicit component registration. This
@@ -213,29 +194,28 @@ struct cpp_type_impl {
213194
// Additionally, implicit registration temporarily resets the scope & with
214195
// state of the world, so that the component is not implicitly created with
215196
// the scope/with of the code it happens to be first used by.
216-
static id_t id(world_t *world = nullptr, const char *name = nullptr,
197+
static id_t id(world_t *world, const char *name = nullptr,
217198
bool allow_tag = true)
218199
{
219-
// If no id has been registered yet, do it now.
220-
if (!registered(world)) {
221-
ecs_entity_t prev_scope = 0;
222-
ecs_id_t prev_with = 0;
223-
224-
if (world) {
225-
prev_scope = ecs_set_scope(world, 0);
226-
prev_with = ecs_set_with(world, 0);
227-
}
200+
ecs_assert(world != nullptr, ECS_INTERNAL_ERROR, name);
201+
202+
if (const ecs_cached_component_info_t* info = ecs_lookup_cached_component_info(world, cached_component_index.index)) {
203+
return info->component;
204+
} else {
205+
// If no id has been registered yet, do it now.
206+
const ecs_entity_t prev_scope = ecs_set_scope(world, 0);
207+
const ecs_id_t prev_with = ecs_set_with(world, 0);
228208

229209
// This will register a component id, but will not register
230210
// lifecycle callbacks.
231211
bool existing;
232-
id_explicit(world, name, allow_tag, 0, true, &existing);
212+
const entity_t id = id_explicit(world, name, allow_tag, 0, true, &existing);
233213

234214
// Register lifecycle callbacks, but only if the component has a
235215
// size. Components that don't have a size are tags, and tags don't
236216
// require construction/destruction/copy/move's. */
237217
if (size() && !existing) {
238-
register_lifecycle_actions<T>(world, s_id);
218+
register_lifecycle_actions<T>(world, id);
239219
}
240220

241221
if (prev_with) {
@@ -244,62 +224,40 @@ struct cpp_type_impl {
244224
if (prev_scope) {
245225
ecs_set_scope(world, prev_scope);
246226
}
247-
}
248227

249-
// By now we should have a valid identifier
250-
ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL);
251-
252-
return s_id;
228+
return id;
229+
}
253230
}
254231

255-
// Return the size of a component.
256-
static size_t size() {
257-
ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL);
258-
return s_size;
232+
/// Looks the assigned component up in the provided world.
233+
/// It can happen that the component has not been initialized yet.
234+
static entity_t lookup(const world_t* world) {
235+
const ecs_cached_component_info_t* info = ecs_lookup_cached_component_info(world, cached_component_index.index);
236+
return info ? info->component : 0;
259237
}
260238

261-
// Return the alignment of a component.
262-
static size_t alignment() {
263-
ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL);
264-
return s_alignment;
239+
// Was the component already registered.
240+
static bool registered(const world_t* world) {
241+
return !!lookup(world);
265242
}
266243

267-
// Was the component already registered.
268-
static bool registered(flecs::world_t *world) {
269-
if (s_reset_count != ecs_cpp_reset_count_get()) {
270-
reset();
271-
}
272-
if (s_id == 0) {
273-
return false;
274-
}
275-
if (world && !ecs_exists(world, s_id)) {
276-
return false;
277-
}
278-
return true;
244+
// Return the size of this component.
245+
static size_t size() {
246+
return sizeof(T);
279247
}
280248

281-
// This function is only used to test cross-translation unit features. No
282-
// code other than test cases should invoke this function.
283-
static void reset() {
284-
s_id = 0;
285-
s_size = 0;
286-
s_alignment = 0;
287-
s_allow_tag = true;
249+
// Return the alignment of this component.
250+
static size_t alignment() {
251+
return alignof(T);
288252
}
289253

290-
static entity_t s_id;
291-
static size_t s_size;
292-
static size_t s_alignment;
293-
static bool s_allow_tag;
294-
static int32_t s_reset_count;
254+
// Acquire a per instance incremental index for a world-local component index cache.
255+
static cpp_type_component_cache_index cached_component_index;
295256
};
296257

297258
// Global templated variables that hold component identifier and other info
298-
template <typename T> entity_t cpp_type_impl<T>::s_id;
299-
template <typename T> size_t cpp_type_impl<T>::s_size;
300-
template <typename T> size_t cpp_type_impl<T>::s_alignment;
301-
template <typename T> bool cpp_type_impl<T>::s_allow_tag( true );
302-
template <typename T> int32_t cpp_type_impl<T>::s_reset_count;
259+
template <typename T>
260+
cpp_type_component_cache_index cpp_type_impl<T>::cached_component_index;
303261

304262
// Front facing class for implicitly registering a component & obtaining
305263
// static component data
@@ -375,10 +333,9 @@ struct component : untyped_component {
375333
implicit_name = true;
376334
}
377335

378-
if (_::cpp_type<T>::registered(world)) {
379-
/* Obtain component id. Because the component is already registered,
380-
* this operation does nothing besides returning the existing id */
381-
id = _::cpp_type<T>::id_explicit(world, name, allow_tag, id);
336+
/* Obtain a registered component id. */
337+
if (const entity_t registered = _::cpp_type<T>::lookup(world)) {
338+
id = registered;
382339

383340
ecs_cpp_component_validate(world, id, n, _::symbol_name<T>(),
384341
_::cpp_type<T>::size(),
@@ -504,17 +461,6 @@ struct component : untyped_component {
504461
}
505462
};
506463

507-
/** Get id currently assigned to component. If no world has registered the
508-
* component yet, this operation will return 0. */
509-
template <typename T>
510-
flecs::entity_t type_id() {
511-
if (_::cpp_type<T>::s_reset_count == ecs_cpp_reset_count_get()) {
512-
return _::cpp_type<T>::s_id;
513-
} else {
514-
return 0;
515-
}
516-
}
517-
518464
/** Reset static component ids.
519465
* When components are registered their component ids are stored in a static
520466
* type specific variable. This stored id is passed into component registration
@@ -537,9 +483,9 @@ flecs::entity_t type_id() {
537483
*
538484
* \ingroup cpp_components
539485
*/
540-
inline void reset() {
541-
ecs_cpp_reset_count_inc();
542-
}
486+
ECS_DEPRECATED("reset was deprecated, world-local component ids "
487+
"are supported by default now.")
488+
inline void reset() {}
543489

544490
}
545491

0 commit comments

Comments
 (0)