@@ -965,6 +965,9 @@ struct ecs_world_t {
965965 ecs_id_record_t *idr_childof_wildcard;
966966 ecs_id_record_t *idr_identifier_name;
967967
968+ /* Head of list that points to all non-fragmenting component ids */
969+ ecs_id_record_t *idr_non_fragmenting_head;
970+
968971 /* -- Mixins -- */
969972 ecs_world_t *self;
970973 ecs_observable_t observable;
@@ -1164,6 +1167,7 @@ struct ecs_id_record_t {
11641167 ecs_id_record_elem_t first; /* (R, *) */
11651168 ecs_id_record_elem_t second; /* (*, O) */
11661169 ecs_id_record_elem_t trav; /* (*, O) with only traversable relationships */
1170+ ecs_id_record_elem_t non_fragmenting; /* All non-fragmenting ids */
11671171
11681172 /* Parent id record. For pair records the parent is the (R, *) record. */
11691173 ecs_id_record_t *parent;
@@ -1241,6 +1245,11 @@ void flecs_id_record_init_sparse(
12411245 ecs_world_t *world,
12421246 ecs_id_record_t *idr);
12431247
1248+ /* Init non-fragmenting component id */
1249+ void flecs_id_record_init_dont_fragment(
1250+ ecs_world_t *world,
1251+ ecs_id_record_t *idr);
1252+
12441253/* Bootstrap cached id records */
12451254void flecs_init_id_records(
12461255 ecs_world_t *world);
@@ -3103,6 +3112,11 @@ void flecs_add_ids(
31033112 ecs_id_t *ids,
31043113 int32_t count);
31053114
3115+ void flecs_entity_remove_non_fragmenting(
3116+ ecs_world_t *world,
3117+ ecs_entity_t e,
3118+ ecs_record_t *r);
3119+
31063120////////////////////////////////////////////////////////////////////////////////
31073121//// Query API
31083122////////////////////////////////////////////////////////////////////////////////
@@ -3464,6 +3478,9 @@ bool flecs_set_id_flag(
34643478 if (flag == EcsIdIsSparse) {
34653479 flecs_id_record_init_sparse(world, idr);
34663480 }
3481+ if (flag == EcsIdDontFragment) {
3482+ flecs_id_record_init_dont_fragment(world, idr);
3483+ }
34673484 return true;
34683485 }
34693486 return false;
@@ -5147,6 +5164,45 @@ void flecs_sparse_on_remove(
51475164 }
51485165}
51495166
5167+ void flecs_entity_remove_non_fragmenting(
5168+ ecs_world_t *world,
5169+ ecs_entity_t e,
5170+ ecs_record_t *r)
5171+ {
5172+ if (!r) {
5173+ r = flecs_entities_get(world, e);
5174+ }
5175+
5176+ if (!r || !(r->row & EcsEntityHasDontFragment)) {
5177+ return;
5178+ }
5179+
5180+ ecs_id_record_t *cur = world->idr_non_fragmenting_head;
5181+ while (cur) {
5182+ ecs_assert(cur->flags & EcsIdIsSparse, ECS_INTERNAL_ERROR, NULL);
5183+ if (cur->sparse) {
5184+ void *ptr = flecs_sparse_get_any(cur->sparse, 0, e);
5185+ if (ptr) {
5186+ const ecs_type_info_t *ti = cur->type_info;
5187+ ecs_xtor_t dtor = ti->hooks.dtor;
5188+ ecs_iter_action_t on_remove = ti->hooks.on_remove;
5189+ if (on_remove) {
5190+ flecs_invoke_hook(world, NULL, NULL, 1, 0,
5191+ &e, cur->id, ti, EcsOnRemove, on_remove);
5192+ }
5193+ if (dtor) {
5194+ dtor(ptr, 1, ti);
5195+ }
5196+ flecs_sparse_remove_fast(cur->sparse, 0, e);
5197+ }
5198+ }
5199+
5200+ cur = cur->non_fragmenting.next;
5201+ }
5202+
5203+ r->row &= ~EcsEntityHasDontFragment;
5204+ }
5205+
51505206static
51515207void flecs_union_on_add(
51525208 ecs_world_t *world,
@@ -5223,6 +5279,14 @@ void flecs_notify_on_add(
52235279
52245280 if (sparse && (diff_flags & EcsTableHasSparse)) {
52255281 flecs_sparse_on_add(world, table, row, count, added, construct);
5282+ if (diff_flags & EcsTableHasDontFragment) {
5283+ int32_t i;
5284+ const ecs_entity_t *entities = ecs_table_entities(table);
5285+ for (i = row; i < (row + count); i ++) {
5286+ ecs_record_t *r = flecs_entities_get(world, entities[i]);
5287+ r->row |= EcsEntityHasDontFragment;
5288+ }
5289+ }
52265290 }
52275291
52285292 if (diff_flags & EcsTableHasUnion) {
@@ -5487,8 +5551,10 @@ void flecs_commit(
54875551 /* If source and destination table are the same no action is needed *
54885552 * However, if a component was added in the process of traversing a
54895553 * table, this suggests that a union relationship could have changed. */
5490- if (src_table && src_table->flags & EcsTableHasUnion) {
5491- diff->added_flags |= EcsIdIsUnion;
5554+ ecs_flags32_t non_fragment_flags =
5555+ src_table->flags & (EcsTableHasUnion|EcsTableHasDontFragment);
5556+ if (src_table && non_fragment_flags) {
5557+ diff->added_flags |= non_fragment_flags;
54925558 flecs_notify_on_add(world, src_table, src_table,
54935559 ECS_RECORD_TO_ROW(record->row), 1, diff, evt_flags, 0,
54945560 construct, true);
@@ -6883,6 +6949,8 @@ void ecs_clear(
68836949 if (r->row & EcsEntityIsTraversable) {
68846950 flecs_table_traversable_add(table, -1);
68856951 }
6952+
6953+ flecs_entity_remove_non_fragmenting(world, entity, NULL);
68866954 }
68876955
68886956 flecs_defer_end(world, stage);
@@ -7444,6 +7512,9 @@ void ecs_delete(
74447512 flecs_table_traversable_add(table, -1);
74457513 }
74467514 }
7515+
7516+ flecs_entity_remove_non_fragmenting(world, entity, r);
7517+
74477518 /* Merge operations before deleting entity */
74487519 flecs_defer_end(world, stage);
74497520 flecs_defer_begin(world, stage);
@@ -35737,6 +35808,17 @@ void flecs_id_record_init_sparse(
3573735808 }
3573835809}
3573935810
35811+ void flecs_id_record_init_dont_fragment(
35812+ ecs_world_t *world,
35813+ ecs_id_record_t *idr)
35814+ {
35815+ if (world->idr_non_fragmenting_head) {
35816+ world->idr_non_fragmenting_head->non_fragmenting.prev = idr;
35817+ }
35818+ idr->non_fragmenting.next = world->idr_non_fragmenting_head;
35819+ world->idr_non_fragmenting_head = idr;
35820+ }
35821+
3574035822static
3574135823void flecs_id_record_fini_sparse(
3574235824 ecs_world_t *world,
@@ -35960,6 +36042,9 @@ ecs_id_record_t* flecs_id_record_new(
3596036042 if (ecs_has_id(world, tgt, EcsSparse)) {
3596136043 idr->flags |= EcsIdIsSparse;
3596236044 }
36045+ if (ecs_has_id(world, tgt, EcsDontFragment)) {
36046+ idr->flags |= EcsIdDontFragment;
36047+ }
3596336048 }
3596436049 }
3596536050
@@ -35973,6 +36058,10 @@ ecs_id_record_t* flecs_id_record_new(
3597336058 }
3597436059 }
3597536060
36061+ if (idr->flags & EcsIdDontFragment) {
36062+ flecs_id_record_init_dont_fragment(world, idr);
36063+ }
36064+
3597636065 if (ecs_should_log_1()) {
3597736066 char *id_str = ecs_id_str(world, id);
3597836067 ecs_dbg_1("#[green]id#[normal] %s #[green]created", id_str);
@@ -37217,6 +37306,8 @@ void flecs_table_dtor_all(
3721737306 ecs_assert(!e || ecs_is_valid(world, e),
3721837307 ECS_INTERNAL_ERROR, NULL);
3721937308
37309+ flecs_entity_remove_non_fragmenting(world, e, NULL);
37310+
3722037311 if (is_delete) {
3722137312 flecs_entities_remove(world, e);
3722237313 ecs_assert(ecs_is_valid(world, e) == false,
@@ -37237,6 +37328,7 @@ void flecs_table_dtor_all(
3723737328 for (i = row; i < end; i ++) {
3723837329 ecs_entity_t e = entities[i];
3723937330 ecs_assert(!e || ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL);
37331+ flecs_entity_remove_non_fragmenting(world, e, NULL);
3724037332 flecs_entities_remove(world, e);
3724137333 ecs_assert(!ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL);
3724237334 }
@@ -37245,6 +37337,7 @@ void flecs_table_dtor_all(
3724537337 ecs_entity_t e = entities[i];
3724637338 ecs_assert(!e || ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL);
3724737339 ecs_record_t *record = flecs_entities_get(world, e);
37340+ flecs_entity_remove_non_fragmenting(world, e, record);
3724837341 record->table = NULL;
3724937342 record->row = record->row & ECS_ROW_FLAGS_MASK;
3725037343 (void)e;
@@ -39795,6 +39888,17 @@ void flecs_compute_table_diff(
3979539888 return;
3979639889 }
3979739890 }
39891+ } else {
39892+ ecs_id_record_t *idr = flecs_id_record_get(world, id);
39893+ if (idr->flags & EcsIdDontFragment) {
39894+ ecs_table_diff_t *diff = flecs_bcalloc(
39895+ &world->allocators.table_diff);
39896+ diff->added.count = 1;
39897+ diff->added.array = flecs_wdup_n(world, ecs_id_t, 1, &id);
39898+ diff->added_flags = EcsTableHasDontFragment|EcsTableHasSparse;
39899+ edge->diff = diff;
39900+ return;
39901+ }
3979839902 }
3979939903
3980039904 ecs_id_t *ids_node = node_type.array;
@@ -39990,7 +40094,6 @@ void flecs_add_with_property(
3999040094 flecs_add_with_property(world, idr_with_wildcard, dst_type, ra, o);
3999140095 }
3999240096 }
39993-
3999440097}
3999540098
3999640099static
@@ -40027,6 +40130,12 @@ ecs_table_t* flecs_find_table_with(
4002740130 r = with;
4002840131 }
4002940132
40133+ if (idr->flags & EcsIdDontFragment) {
40134+ /* Component doesn't fragment tables */
40135+ node->flags |= EcsTableHasDontFragment;
40136+ return node;
40137+ }
40138+
4003040139 /* Create sequence with new id */
4003140140 ecs_type_t dst_type;
4003240141 int res = flecs_type_new_with(world, &dst_type, &node->type, with);
@@ -40060,14 +40169,21 @@ ecs_table_t* flecs_find_table_without(
4006040169 ecs_table_t *node,
4006140170 ecs_id_t without)
4006240171{
40172+ ecs_id_record_t *idr = NULL;
40173+
4006340174 if (ECS_IS_PAIR(without)) {
4006440175 ecs_entity_t r = 0;
40065- ecs_id_record_t *idr = NULL;
4006640176 r = ECS_PAIR_FIRST(without);
4006740177 idr = flecs_id_record_get(world, ecs_pair(r, EcsWildcard));
4006840178 if (idr && idr->flags & EcsIdIsUnion) {
4006940179 without = ecs_pair(r, EcsUnion);
4007040180 }
40181+ } else {
40182+ idr = flecs_id_record_get(world, without);
40183+ if (idr && idr->flags & EcsIdDontFragment) {
40184+ /* Component doesn't fragment tables */
40185+ return node;
40186+ }
4007140187 }
4007240188
4007340189 /* Create sequence with new id */
@@ -40110,7 +40226,7 @@ void flecs_init_edge_for_add(
4011040226
4011140227 flecs_table_ensure_hi_edge(world, &table->node.add, id);
4011240228
40113- if ((table != to) || (table->flags & EcsTableHasUnion)) {
40229+ if ((table != to) || (table->flags & ( EcsTableHasUnion|EcsTableHasDontFragment) )) {
4011440230 /* Add edges are appended to refs.next */
4011540231 ecs_graph_edge_hdr_t *to_refs = &to->node.refs;
4011640232 ecs_graph_edge_hdr_t *next = to_refs->next;
0 commit comments