Skip to content

Commit

Permalink
Implement non-fragmenting component storage
Browse files Browse the repository at this point in the history
  • Loading branch information
SanderMertens committed Feb 15, 2025
1 parent 2a337a6 commit 4bac4d4
Show file tree
Hide file tree
Showing 12 changed files with 351 additions and 12 deletions.
126 changes: 121 additions & 5 deletions distr/flecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,9 @@ struct ecs_world_t {
ecs_id_record_t *idr_childof_wildcard;
ecs_id_record_t *idr_identifier_name;

/* Head of list that points to all non-fragmenting component ids */
ecs_id_record_t *idr_non_fragmenting_head;

/* -- Mixins -- */
ecs_world_t *self;
ecs_observable_t observable;
Expand Down Expand Up @@ -1164,6 +1167,7 @@ struct ecs_id_record_t {
ecs_id_record_elem_t first; /* (R, *) */
ecs_id_record_elem_t second; /* (*, O) */
ecs_id_record_elem_t trav; /* (*, O) with only traversable relationships */
ecs_id_record_elem_t non_fragmenting; /* All non-fragmenting ids */

/* Parent id record. For pair records the parent is the (R, *) record. */
ecs_id_record_t *parent;
Expand Down Expand Up @@ -1241,6 +1245,11 @@ void flecs_id_record_init_sparse(
ecs_world_t *world,
ecs_id_record_t *idr);

/* Init non-fragmenting component id */
void flecs_id_record_init_dont_fragment(
ecs_world_t *world,
ecs_id_record_t *idr);

/* Bootstrap cached id records */
void flecs_init_id_records(
ecs_world_t *world);
Expand Down Expand Up @@ -3103,6 +3112,11 @@ void flecs_add_ids(
ecs_id_t *ids,
int32_t count);

void flecs_entity_remove_non_fragmenting(
ecs_world_t *world,
ecs_entity_t e,
ecs_record_t *r);

////////////////////////////////////////////////////////////////////////////////
//// Query API
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -3464,6 +3478,9 @@ bool flecs_set_id_flag(
if (flag == EcsIdIsSparse) {
flecs_id_record_init_sparse(world, idr);
}
if (flag == EcsIdDontFragment) {
flecs_id_record_init_dont_fragment(world, idr);
}
return true;
}
return false;
Expand Down Expand Up @@ -5147,6 +5164,45 @@ void flecs_sparse_on_remove(
}
}

void flecs_entity_remove_non_fragmenting(
ecs_world_t *world,
ecs_entity_t e,
ecs_record_t *r)
{
if (!r) {
r = flecs_entities_get(world, e);
}

if (!r || !(r->row & EcsEntityHasDontFragment)) {
return;
}

ecs_id_record_t *cur = world->idr_non_fragmenting_head;
while (cur) {
ecs_assert(cur->flags & EcsIdIsSparse, ECS_INTERNAL_ERROR, NULL);
if (cur->sparse) {
void *ptr = flecs_sparse_get_any(cur->sparse, 0, e);
if (ptr) {
const ecs_type_info_t *ti = cur->type_info;
ecs_xtor_t dtor = ti->hooks.dtor;
ecs_iter_action_t on_remove = ti->hooks.on_remove;
if (on_remove) {
flecs_invoke_hook(world, NULL, NULL, 1, 0,
&e, cur->id, ti, EcsOnRemove, on_remove);
}
if (dtor) {
dtor(ptr, 1, ti);
}
flecs_sparse_remove_fast(cur->sparse, 0, e);
}
}

cur = cur->non_fragmenting.next;
}

r->row &= ~EcsEntityHasDontFragment;
}

static
void flecs_union_on_add(
ecs_world_t *world,
Expand Down Expand Up @@ -5223,6 +5279,14 @@ void flecs_notify_on_add(

if (sparse && (diff_flags & EcsTableHasSparse)) {
flecs_sparse_on_add(world, table, row, count, added, construct);
if (diff_flags & EcsTableHasDontFragment) {
int32_t i;
const ecs_entity_t *entities = ecs_table_entities(table);
for (i = row; i < (row + count); i ++) {
ecs_record_t *r = flecs_entities_get(world, entities[i]);
r->row |= EcsEntityHasDontFragment;
}
}
}

if (diff_flags & EcsTableHasUnion) {
Expand Down Expand Up @@ -5487,8 +5551,10 @@ void flecs_commit(
/* If source and destination table are the same no action is needed *
* However, if a component was added in the process of traversing a
* table, this suggests that a union relationship could have changed. */
if (src_table && src_table->flags & EcsTableHasUnion) {
diff->added_flags |= EcsIdIsUnion;
ecs_flags32_t non_fragment_flags =
src_table->flags & (EcsTableHasUnion|EcsTableHasDontFragment);
if (src_table && non_fragment_flags) {
diff->added_flags |= non_fragment_flags;
flecs_notify_on_add(world, src_table, src_table,
ECS_RECORD_TO_ROW(record->row), 1, diff, evt_flags, 0,
construct, true);
Expand Down Expand Up @@ -6883,6 +6949,8 @@ void ecs_clear(
if (r->row & EcsEntityIsTraversable) {
flecs_table_traversable_add(table, -1);
}

flecs_entity_remove_non_fragmenting(world, entity, NULL);
}

flecs_defer_end(world, stage);
Expand Down Expand Up @@ -7444,6 +7512,9 @@ void ecs_delete(
flecs_table_traversable_add(table, -1);
}
}

flecs_entity_remove_non_fragmenting(world, entity, r);

/* Merge operations before deleting entity */
flecs_defer_end(world, stage);
flecs_defer_begin(world, stage);
Expand Down Expand Up @@ -35737,6 +35808,17 @@ void flecs_id_record_init_sparse(
}
}

void flecs_id_record_init_dont_fragment(
ecs_world_t *world,
ecs_id_record_t *idr)
{
if (world->idr_non_fragmenting_head) {
world->idr_non_fragmenting_head->non_fragmenting.prev = idr;
}
idr->non_fragmenting.next = world->idr_non_fragmenting_head;
world->idr_non_fragmenting_head = idr;
}

static
void flecs_id_record_fini_sparse(
ecs_world_t *world,
Expand Down Expand Up @@ -35960,6 +36042,9 @@ ecs_id_record_t* flecs_id_record_new(
if (ecs_has_id(world, tgt, EcsSparse)) {
idr->flags |= EcsIdIsSparse;
}
if (ecs_has_id(world, tgt, EcsDontFragment)) {
idr->flags |= EcsIdDontFragment;
}
}
}

Expand All @@ -35973,6 +36058,10 @@ ecs_id_record_t* flecs_id_record_new(
}
}

if (idr->flags & EcsIdDontFragment) {
flecs_id_record_init_dont_fragment(world, idr);
}

if (ecs_should_log_1()) {
char *id_str = ecs_id_str(world, id);
ecs_dbg_1("#[green]id#[normal] %s #[green]created", id_str);
Expand Down Expand Up @@ -37217,6 +37306,8 @@ void flecs_table_dtor_all(
ecs_assert(!e || ecs_is_valid(world, e),
ECS_INTERNAL_ERROR, NULL);

flecs_entity_remove_non_fragmenting(world, e, NULL);

if (is_delete) {
flecs_entities_remove(world, e);
ecs_assert(ecs_is_valid(world, e) == false,
Expand All @@ -37237,6 +37328,7 @@ void flecs_table_dtor_all(
for (i = row; i < end; i ++) {
ecs_entity_t e = entities[i];
ecs_assert(!e || ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL);
flecs_entity_remove_non_fragmenting(world, e, NULL);
flecs_entities_remove(world, e);
ecs_assert(!ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL);
}
Expand All @@ -37245,6 +37337,7 @@ void flecs_table_dtor_all(
ecs_entity_t e = entities[i];
ecs_assert(!e || ecs_is_valid(world, e), ECS_INTERNAL_ERROR, NULL);
ecs_record_t *record = flecs_entities_get(world, e);
flecs_entity_remove_non_fragmenting(world, e, record);
record->table = NULL;
record->row = record->row & ECS_ROW_FLAGS_MASK;
(void)e;
Expand Down Expand Up @@ -39795,6 +39888,17 @@ void flecs_compute_table_diff(
return;
}
}
} else {
ecs_id_record_t *idr = flecs_id_record_get(world, id);
if (idr->flags & EcsIdDontFragment) {
ecs_table_diff_t *diff = flecs_bcalloc(
&world->allocators.table_diff);
diff->added.count = 1;
diff->added.array = flecs_wdup_n(world, ecs_id_t, 1, &id);
diff->added_flags = EcsTableHasDontFragment|EcsTableHasSparse;
edge->diff = diff;
return;
}
}

ecs_id_t *ids_node = node_type.array;
Expand Down Expand Up @@ -39990,7 +40094,6 @@ void flecs_add_with_property(
flecs_add_with_property(world, idr_with_wildcard, dst_type, ra, o);
}
}

}

static
Expand Down Expand Up @@ -40027,6 +40130,12 @@ ecs_table_t* flecs_find_table_with(
r = with;
}

if (idr->flags & EcsIdDontFragment) {
/* Component doesn't fragment tables */
node->flags |= EcsTableHasDontFragment;
return node;
}

/* Create sequence with new id */
ecs_type_t dst_type;
int res = flecs_type_new_with(world, &dst_type, &node->type, with);
Expand Down Expand Up @@ -40060,14 +40169,21 @@ ecs_table_t* flecs_find_table_without(
ecs_table_t *node,
ecs_id_t without)
{
ecs_id_record_t *idr = NULL;

if (ECS_IS_PAIR(without)) {
ecs_entity_t r = 0;
ecs_id_record_t *idr = NULL;
r = ECS_PAIR_FIRST(without);
idr = flecs_id_record_get(world, ecs_pair(r, EcsWildcard));
if (idr && idr->flags & EcsIdIsUnion) {
without = ecs_pair(r, EcsUnion);
}
} else {
idr = flecs_id_record_get(world, without);
if (idr && idr->flags & EcsIdDontFragment) {
/* Component doesn't fragment tables */
return node;
}
}

/* Create sequence with new id */
Expand Down Expand Up @@ -40110,7 +40226,7 @@ void flecs_init_edge_for_add(

flecs_table_ensure_hi_edge(world, &table->node.add, id);

if ((table != to) || (table->flags & EcsTableHasUnion)) {
if ((table != to) || (table->flags & (EcsTableHasUnion|EcsTableHasDontFragment))) {
/* Add edges are appended to refs.next */
ecs_graph_edge_hdr_t *to_refs = &to->node.refs;
ecs_graph_edge_hdr_t *next = to_refs->next;
Expand Down
3 changes: 2 additions & 1 deletion distr/flecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ extern "C" {
#define EcsEntityIsId (1u << 31)
#define EcsEntityIsTarget (1u << 30)
#define EcsEntityIsTraversable (1u << 29)
#define EcsEntityHasDontFragment (1u << 28)


////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -571,7 +572,7 @@ extern "C" {
#define EcsTableHasOnTableCreate (1u << 21u)
#define EcsTableHasOnTableDelete (1u << 22u)
#define EcsTableHasSparse (1u << 23u)
// #define EcsTableHasDontFragment (1u << 24u) /* Can't happen */
#define EcsTableHasDontFragment (1u << 24u)
#define EcsTableHasUnion (1u << 25u)

#define EcsTableHasTraversable (1u << 26u)
Expand Down
3 changes: 2 additions & 1 deletion include/flecs/private/api_flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ extern "C" {
#define EcsEntityIsId (1u << 31)
#define EcsEntityIsTarget (1u << 30)
#define EcsEntityIsTraversable (1u << 29)
#define EcsEntityHasDontFragment (1u << 28)


////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -221,7 +222,7 @@ extern "C" {
#define EcsTableHasOnTableCreate (1u << 21u)
#define EcsTableHasOnTableDelete (1u << 22u)
#define EcsTableHasSparse (1u << 23u)
// #define EcsTableHasDontFragment (1u << 24u) /* Can't happen */
#define EcsTableHasDontFragment (1u << 24u)
#define EcsTableHasUnion (1u << 25u)

#define EcsTableHasTraversable (1u << 26u)
Expand Down
3 changes: 3 additions & 0 deletions src/bootstrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ bool flecs_set_id_flag(
if (flag == EcsIdIsSparse) {
flecs_id_record_init_sparse(world, idr);
}
if (flag == EcsIdDontFragment) {
flecs_id_record_init_dont_fragment(world, idr);
}
return true;
}
return false;
Expand Down
Loading

0 comments on commit 4bac4d4

Please # to comment.