Skip to content

Commit 7600975

Browse files
authored
Merge pull request #3 from VerdantInteractive/or-toggle-fix
Fix toggle mask compilation and evaluation with ored terms
2 parents 449f1b0 + e7e3d33 commit 7600975

6 files changed

Lines changed: 261 additions & 11 deletions

File tree

distr/flecs.c

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79960,13 +79960,15 @@ int flecs_query_insert_toggle(
7996079960
continue;
7996179961
}
7996279962

79963+
ecs_flags64_t field_bit = 1llu << term->field_index;
79964+
7996379965
/* Source matches, set flag */
7996479966
if (term->oper == EcsNot) {
79965-
not_toggles |= (1llu << j);
79967+
not_toggles |= field_bit;
7996679968
} else if (term->oper == EcsOptional) {
79967-
optional_toggles |= (1llu << j);
79969+
optional_toggles |= field_bit;
7996879970
} else {
79969-
and_toggles |= (1llu << j);
79971+
and_toggles |= field_bit;
7997079972
}
7997179973

7997279974
fields_done |= (1llu << j);
@@ -79999,18 +80001,24 @@ int flecs_query_insert_toggle(
7999980001
* set, separate instructions let the query engine backtrack to get
8000080002
* the right results. */
8000180003
if (optional_toggles) {
80004+
ecs_flags64_t optional_done = 0;
8000280005
for (j = i; j < term_count; j ++) {
80003-
uint64_t field_bit = 1ull << j;
80006+
uint64_t field_bit = 1ull << terms[j].field_index;
8000480007
if (!(optional_toggles & field_bit)) {
8000580008
continue;
8000680009
}
80010+
if (optional_done & field_bit) {
80011+
continue;
80012+
}
8000780013

8000880014
ecs_query_op_t op = {0};
8000980015
op.kind = EcsQueryToggleOption;
8001080016
op.src = cur.src;
8001180017
op.first.entity = field_bit;
8001280018
op.flags = cur.flags;
8001380019
flecs_query_op_insert(&op, ctx);
80020+
80021+
optional_done |= field_bit;
8001480022
}
8001580023
}
8001680024
}
@@ -85194,6 +85202,7 @@ static inline int32_t flecs_ctz64(uint64_t v) {
8519485202
static
8519585203
flecs_query_row_mask_t flecs_query_get_row_mask(
8519685204
ecs_iter_t *it,
85205+
const ecs_query_t *q,
8519785206
ecs_table_t *table,
8519885207
int32_t block_index,
8519985208
ecs_flags64_t and_fields,
@@ -85219,6 +85228,57 @@ flecs_query_row_mask_t flecs_query_get_row_mask(
8521985228
ecs_abort(ECS_INTERNAL_ERROR, NULL);
8522085229
}
8522185230

85231+
ecs_term_t *field_term = NULL;
85232+
int32_t ti;
85233+
for (ti = 0; ti < q->term_count; ti ++) {
85234+
if (q->terms[ti].field_index == i) {
85235+
field_term = &q->terms[ti];
85236+
break;
85237+
}
85238+
}
85239+
85240+
bool is_or = false;
85241+
if (field_term) {
85242+
is_or = field_term->oper == EcsOr ||
85243+
((field_term != q->terms) && (field_term[-1].oper == EcsOr));
85244+
}
85245+
85246+
if (is_or) {
85247+
int32_t start = flecs_itoi32(field_term - q->terms);
85248+
int32_t end = start;
85249+
85250+
while (start && q->terms[start - 1].oper == EcsOr) {
85251+
start --;
85252+
}
85253+
while (end < (q->term_count - 1) && q->terms[end].oper == EcsOr) {
85254+
end ++;
85255+
}
85256+
85257+
ecs_flags64_t block = 0;
85258+
bool chain_has_bitset = false;
85259+
85260+
int32_t j;
85261+
for (j = start; j <= end; j ++) {
85262+
ecs_id_t id = q->terms[j].id;
85263+
ecs_bitset_t *bs = flecs_table_get_toggle(table, id);
85264+
if (bs) {
85265+
ecs_assert((64 * block_index) < bs->size, ECS_INTERNAL_ERROR, NULL);
85266+
block |= bs->data[block_index];
85267+
chain_has_bitset = true;
85268+
} else if (ecs_table_has_id(it->world, table, id)) {
85269+
block = UINT64_MAX;
85270+
break;
85271+
}
85272+
}
85273+
85274+
if (chain_has_bitset || block == UINT64_MAX) {
85275+
mask &= block;
85276+
has_bitset = has_bitset || chain_has_bitset;
85277+
}
85278+
85279+
continue;
85280+
}
85281+
8522285282
ecs_id_t id = it->ids[i];
8522385283
ecs_bitset_t *bs = flecs_table_get_toggle(table, id);
8522485284
if (!bs) {
@@ -85368,7 +85428,8 @@ bool flecs_query_toggle_cmp(
8536885428
block_index = op_ctx->block_index = new_block_index;
8536985429

8537085430
flecs_query_row_mask_t row_mask = flecs_query_get_row_mask(
85371-
it, table, block_index, and_fields, not_fields, op_ctx);
85431+
it, &ctx->query->pub, table, block_index,
85432+
and_fields, not_fields, op_ctx);
8537285433

8537385434
/* If table doesn't have bitset columns, all columns match */
8537485435
if (!(op_ctx->has_bitset = row_mask.has_bitset)) {

src/query/compiler/compiler.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -770,13 +770,15 @@ int flecs_query_insert_toggle(
770770
continue;
771771
}
772772

773+
ecs_flags64_t field_bit = 1llu << term->field_index;
774+
773775
/* Source matches, set flag */
774776
if (term->oper == EcsNot) {
775-
not_toggles |= (1llu << j);
777+
not_toggles |= field_bit;
776778
} else if (term->oper == EcsOptional) {
777-
optional_toggles |= (1llu << j);
779+
optional_toggles |= field_bit;
778780
} else {
779-
and_toggles |= (1llu << j);
781+
and_toggles |= field_bit;
780782
}
781783

782784
fields_done |= (1llu << j);
@@ -809,18 +811,24 @@ int flecs_query_insert_toggle(
809811
* set, separate instructions let the query engine backtrack to get
810812
* the right results. */
811813
if (optional_toggles) {
814+
ecs_flags64_t optional_done = 0;
812815
for (j = i; j < term_count; j ++) {
813-
uint64_t field_bit = 1ull << j;
816+
uint64_t field_bit = 1ull << terms[j].field_index;
814817
if (!(optional_toggles & field_bit)) {
815818
continue;
816819
}
820+
if (optional_done & field_bit) {
821+
continue;
822+
}
817823

818824
ecs_query_op_t op = {0};
819825
op.kind = EcsQueryToggleOption;
820826
op.src = cur.src;
821827
op.first.entity = field_bit;
822828
op.flags = cur.flags;
823829
flecs_query_op_insert(&op, ctx);
830+
831+
optional_done |= field_bit;
824832
}
825833
}
826834
}

src/query/engine/eval_toggle.c

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ static inline int32_t flecs_ctz64(uint64_t v) {
3131
static
3232
flecs_query_row_mask_t flecs_query_get_row_mask(
3333
ecs_iter_t *it,
34+
const ecs_query_t *q,
3435
ecs_table_t *table,
3536
int32_t block_index,
3637
ecs_flags64_t and_fields,
@@ -56,6 +57,57 @@ flecs_query_row_mask_t flecs_query_get_row_mask(
5657
ecs_abort(ECS_INTERNAL_ERROR, NULL);
5758
}
5859

60+
ecs_term_t *field_term = NULL;
61+
int32_t ti;
62+
for (ti = 0; ti < q->term_count; ti ++) {
63+
if (q->terms[ti].field_index == i) {
64+
field_term = &q->terms[ti];
65+
break;
66+
}
67+
}
68+
69+
bool is_or = false;
70+
if (field_term) {
71+
is_or = field_term->oper == EcsOr ||
72+
((field_term != q->terms) && (field_term[-1].oper == EcsOr));
73+
}
74+
75+
if (is_or) {
76+
int32_t start = flecs_itoi32(field_term - q->terms);
77+
int32_t end = start;
78+
79+
while (start && q->terms[start - 1].oper == EcsOr) {
80+
start --;
81+
}
82+
while (end < (q->term_count - 1) && q->terms[end].oper == EcsOr) {
83+
end ++;
84+
}
85+
86+
ecs_flags64_t block = 0;
87+
bool chain_has_bitset = false;
88+
89+
int32_t j;
90+
for (j = start; j <= end; j ++) {
91+
ecs_id_t id = q->terms[j].id;
92+
ecs_bitset_t *bs = flecs_table_get_toggle(table, id);
93+
if (bs) {
94+
ecs_assert((64 * block_index) < bs->size, ECS_INTERNAL_ERROR, NULL);
95+
block |= bs->data[block_index];
96+
chain_has_bitset = true;
97+
} else if (ecs_table_has_id(it->world, table, id)) {
98+
block = UINT64_MAX;
99+
break;
100+
}
101+
}
102+
103+
if (chain_has_bitset || block == UINT64_MAX) {
104+
mask &= block;
105+
has_bitset = has_bitset || chain_has_bitset;
106+
}
107+
108+
continue;
109+
}
110+
59111
ecs_id_t id = it->ids[i];
60112
ecs_bitset_t *bs = flecs_table_get_toggle(table, id);
61113
if (!bs) {
@@ -205,7 +257,8 @@ bool flecs_query_toggle_cmp(
205257
block_index = op_ctx->block_index = new_block_index;
206258

207259
flecs_query_row_mask_t row_mask = flecs_query_get_row_mask(
208-
it, table, block_index, and_fields, not_fields, op_ctx);
260+
it, &ctx->query->pub, table, block_index,
261+
and_fields, not_fields, op_ctx);
209262

210263
/* If table doesn't have bitset columns, all columns match */
211264
if (!(op_ctx->has_bitset = row_mask.has_bitset)) {

test/query/project.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2082,6 +2082,8 @@
20822082
"fixed_2_src_w_toggle",
20832083
"this_w_fixed_src_w_toggle",
20842084
"fixed_src_w_this_w_toggle",
2085+
"or_chain_3_toggleable_tags",
2086+
"or_chain_3_toggleable_components",
20852087
"this_from_nothing",
20862088
"this",
20872089
"this_skip_initial",

test/query/src/Toggle.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6165,3 +6165,119 @@ void Toggle_toggle_0_src(void) {
61656165

61666166
ecs_fini(world);
61676167
}
6168+
6169+
static
6170+
int32_t toggle_or_match_count(
6171+
ecs_world_t *world,
6172+
ecs_query_t *q)
6173+
{
6174+
int32_t count = 0;
6175+
ecs_iter_t it = ecs_query_iter(world, q);
6176+
while (ecs_query_next(&it)) {
6177+
count += it.count;
6178+
}
6179+
return count;
6180+
}
6181+
6182+
void Toggle_or_chain_3_toggleable_tags(void) {
6183+
ecs_world_t *world = ecs_mini();
6184+
6185+
ECS_TAG(world, TagA);
6186+
ECS_TAG(world, TagB);
6187+
ECS_TAG(world, TagC);
6188+
6189+
ecs_add_id(world, TagA, EcsCanToggle);
6190+
ecs_add_id(world, TagB, EcsCanToggle);
6191+
ecs_add_id(world, TagC, EcsCanToggle);
6192+
6193+
ecs_entity_t e = ecs_new_w_id(world, TagA);
6194+
ecs_add(world, e, TagB);
6195+
ecs_add(world, e, TagC);
6196+
6197+
ecs_enable_id(world, e, TagA, false);
6198+
ecs_enable_id(world, e, TagB, false);
6199+
ecs_enable_id(world, e, TagC, false);
6200+
6201+
ecs_query_t *q = ecs_query(world, {
6202+
.terms = {
6203+
{ .id = TagA, .oper = EcsOr },
6204+
{ .id = TagB, .oper = EcsOr },
6205+
{ .id = TagC }
6206+
},
6207+
.cache_kind = cache_kind
6208+
});
6209+
test_assert(q != NULL);
6210+
6211+
test_int(toggle_or_match_count(world, q), 0);
6212+
6213+
ecs_enable_id(world, e, TagA, true);
6214+
test_int(toggle_or_match_count(world, q), 1);
6215+
6216+
ecs_enable_id(world, e, TagA, false);
6217+
ecs_enable_id(world, e, TagB, true);
6218+
test_int(toggle_or_match_count(world, q), 1);
6219+
6220+
ecs_enable_id(world, e, TagB, false);
6221+
ecs_enable_id(world, e, TagC, true);
6222+
test_int(toggle_or_match_count(world, q), 1);
6223+
6224+
ecs_enable_id(world, e, TagA, true);
6225+
ecs_enable_id(world, e, TagB, true);
6226+
test_int(toggle_or_match_count(world, q), 1);
6227+
6228+
ecs_query_fini(q);
6229+
6230+
ecs_fini(world);
6231+
}
6232+
6233+
void Toggle_or_chain_3_toggleable_components(void) {
6234+
ecs_world_t *world = ecs_mini();
6235+
6236+
ECS_COMPONENT(world, Position);
6237+
ECS_COMPONENT(world, Velocity);
6238+
ECS_COMPONENT(world, Mass);
6239+
6240+
ecs_add_id(world, ecs_id(Position), EcsCanToggle);
6241+
ecs_add_id(world, ecs_id(Velocity), EcsCanToggle);
6242+
ecs_add_id(world, ecs_id(Mass), EcsCanToggle);
6243+
6244+
ecs_entity_t e = ecs_new_w(world, Position);
6245+
ecs_set(world, e, Position, {10, 20});
6246+
ecs_set(world, e, Velocity, {1, 2});
6247+
ecs_set(world, e, Mass, {100});
6248+
6249+
ecs_enable_component(world, e, Position, false);
6250+
ecs_enable_component(world, e, Velocity, false);
6251+
ecs_enable_component(world, e, Mass, false);
6252+
6253+
ecs_query_t *q = ecs_query(world, {
6254+
.terms = {
6255+
{ .id = ecs_id(Position), .oper = EcsOr },
6256+
{ .id = ecs_id(Velocity), .oper = EcsOr },
6257+
{ .id = ecs_id(Mass) }
6258+
},
6259+
.cache_kind = cache_kind
6260+
});
6261+
test_assert(q != NULL);
6262+
6263+
test_int(toggle_or_match_count(world, q), 0);
6264+
6265+
ecs_enable_component(world, e, Position, true);
6266+
test_int(toggle_or_match_count(world, q), 1);
6267+
6268+
ecs_enable_component(world, e, Position, false);
6269+
ecs_enable_component(world, e, Velocity, true);
6270+
test_int(toggle_or_match_count(world, q), 1);
6271+
6272+
ecs_enable_component(world, e, Velocity, false);
6273+
ecs_enable_component(world, e, Mass, true);
6274+
test_int(toggle_or_match_count(world, q), 1);
6275+
6276+
ecs_enable_component(world, e, Position, true);
6277+
ecs_enable_component(world, e, Velocity, true);
6278+
test_int(toggle_or_match_count(world, q), 1);
6279+
6280+
ecs_query_fini(q);
6281+
6282+
ecs_fini(world);
6283+
}

0 commit comments

Comments
 (0)