Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 66 additions & 5 deletions distr/flecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -79960,13 +79960,15 @@ int flecs_query_insert_toggle(
continue;
}

ecs_flags64_t field_bit = 1llu << term->field_index;

/* Source matches, set flag */
if (term->oper == EcsNot) {
not_toggles |= (1llu << j);
not_toggles |= field_bit;
} else if (term->oper == EcsOptional) {
optional_toggles |= (1llu << j);
optional_toggles |= field_bit;
} else {
and_toggles |= (1llu << j);
and_toggles |= field_bit;
}

fields_done |= (1llu << j);
Expand Down Expand Up @@ -79999,18 +80001,24 @@ int flecs_query_insert_toggle(
* set, separate instructions let the query engine backtrack to get
* the right results. */
if (optional_toggles) {
ecs_flags64_t optional_done = 0;
for (j = i; j < term_count; j ++) {
uint64_t field_bit = 1ull << j;
uint64_t field_bit = 1ull << terms[j].field_index;
if (!(optional_toggles & field_bit)) {
continue;
}
if (optional_done & field_bit) {
continue;
}

ecs_query_op_t op = {0};
op.kind = EcsQueryToggleOption;
op.src = cur.src;
op.first.entity = field_bit;
op.flags = cur.flags;
flecs_query_op_insert(&op, ctx);

optional_done |= field_bit;
}
}
}
Expand Down Expand Up @@ -85194,6 +85202,7 @@ static inline int32_t flecs_ctz64(uint64_t v) {
static
flecs_query_row_mask_t flecs_query_get_row_mask(
ecs_iter_t *it,
const ecs_query_t *q,
ecs_table_t *table,
int32_t block_index,
ecs_flags64_t and_fields,
Expand All @@ -85219,6 +85228,57 @@ flecs_query_row_mask_t flecs_query_get_row_mask(
ecs_abort(ECS_INTERNAL_ERROR, NULL);
}

ecs_term_t *field_term = NULL;
int32_t ti;
for (ti = 0; ti < q->term_count; ti ++) {
if (q->terms[ti].field_index == i) {
field_term = &q->terms[ti];
break;
}
}

bool is_or = false;
if (field_term) {
is_or = field_term->oper == EcsOr ||
((field_term != q->terms) && (field_term[-1].oper == EcsOr));
}

if (is_or) {
int32_t start = flecs_itoi32(field_term - q->terms);
int32_t end = start;

while (start && q->terms[start - 1].oper == EcsOr) {
start --;
}
while (end < (q->term_count - 1) && q->terms[end].oper == EcsOr) {
end ++;
}

ecs_flags64_t block = 0;
bool chain_has_bitset = false;

int32_t j;
for (j = start; j <= end; j ++) {
ecs_id_t id = q->terms[j].id;
ecs_bitset_t *bs = flecs_table_get_toggle(table, id);
if (bs) {
ecs_assert((64 * block_index) < bs->size, ECS_INTERNAL_ERROR, NULL);
block |= bs->data[block_index];
chain_has_bitset = true;
} else if (ecs_table_has_id(it->world, table, id)) {
block = UINT64_MAX;
break;
}
}

if (chain_has_bitset || block == UINT64_MAX) {
mask &= block;
has_bitset = has_bitset || chain_has_bitset;
}

continue;
}

ecs_id_t id = it->ids[i];
ecs_bitset_t *bs = flecs_table_get_toggle(table, id);
if (!bs) {
Expand Down Expand Up @@ -85368,7 +85428,8 @@ bool flecs_query_toggle_cmp(
block_index = op_ctx->block_index = new_block_index;

flecs_query_row_mask_t row_mask = flecs_query_get_row_mask(
it, table, block_index, and_fields, not_fields, op_ctx);
it, &ctx->query->pub, table, block_index,
and_fields, not_fields, op_ctx);

/* If table doesn't have bitset columns, all columns match */
if (!(op_ctx->has_bitset = row_mask.has_bitset)) {
Expand Down
16 changes: 12 additions & 4 deletions src/query/compiler/compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -770,13 +770,15 @@ int flecs_query_insert_toggle(
continue;
}

ecs_flags64_t field_bit = 1llu << term->field_index;

/* Source matches, set flag */
if (term->oper == EcsNot) {
not_toggles |= (1llu << j);
not_toggles |= field_bit;
} else if (term->oper == EcsOptional) {
optional_toggles |= (1llu << j);
optional_toggles |= field_bit;
} else {
and_toggles |= (1llu << j);
and_toggles |= field_bit;
}

fields_done |= (1llu << j);
Expand Down Expand Up @@ -809,18 +811,24 @@ int flecs_query_insert_toggle(
* set, separate instructions let the query engine backtrack to get
* the right results. */
if (optional_toggles) {
ecs_flags64_t optional_done = 0;
for (j = i; j < term_count; j ++) {
uint64_t field_bit = 1ull << j;
uint64_t field_bit = 1ull << terms[j].field_index;
if (!(optional_toggles & field_bit)) {
continue;
}
if (optional_done & field_bit) {
continue;
}

ecs_query_op_t op = {0};
op.kind = EcsQueryToggleOption;
op.src = cur.src;
op.first.entity = field_bit;
op.flags = cur.flags;
flecs_query_op_insert(&op, ctx);

optional_done |= field_bit;
}
}
}
Expand Down
55 changes: 54 additions & 1 deletion src/query/engine/eval_toggle.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ static inline int32_t flecs_ctz64(uint64_t v) {
static
flecs_query_row_mask_t flecs_query_get_row_mask(
ecs_iter_t *it,
const ecs_query_t *q,
ecs_table_t *table,
int32_t block_index,
ecs_flags64_t and_fields,
Expand All @@ -56,6 +57,57 @@ flecs_query_row_mask_t flecs_query_get_row_mask(
ecs_abort(ECS_INTERNAL_ERROR, NULL);
}

ecs_term_t *field_term = NULL;
int32_t ti;
for (ti = 0; ti < q->term_count; ti ++) {
if (q->terms[ti].field_index == i) {
field_term = &q->terms[ti];
break;
}
}

bool is_or = false;
if (field_term) {
is_or = field_term->oper == EcsOr ||
((field_term != q->terms) && (field_term[-1].oper == EcsOr));
}

if (is_or) {
int32_t start = flecs_itoi32(field_term - q->terms);
int32_t end = start;

while (start && q->terms[start - 1].oper == EcsOr) {
start --;
}
while (end < (q->term_count - 1) && q->terms[end].oper == EcsOr) {
end ++;
}

ecs_flags64_t block = 0;
bool chain_has_bitset = false;

int32_t j;
for (j = start; j <= end; j ++) {
ecs_id_t id = q->terms[j].id;
ecs_bitset_t *bs = flecs_table_get_toggle(table, id);
if (bs) {
ecs_assert((64 * block_index) < bs->size, ECS_INTERNAL_ERROR, NULL);
block |= bs->data[block_index];
chain_has_bitset = true;
} else if (ecs_table_has_id(it->world, table, id)) {
block = UINT64_MAX;
break;
}
}

if (chain_has_bitset || block == UINT64_MAX) {
mask &= block;
has_bitset = has_bitset || chain_has_bitset;
}

continue;
}

ecs_id_t id = it->ids[i];
ecs_bitset_t *bs = flecs_table_get_toggle(table, id);
if (!bs) {
Expand Down Expand Up @@ -205,7 +257,8 @@ bool flecs_query_toggle_cmp(
block_index = op_ctx->block_index = new_block_index;

flecs_query_row_mask_t row_mask = flecs_query_get_row_mask(
it, table, block_index, and_fields, not_fields, op_ctx);
it, &ctx->query->pub, table, block_index,
and_fields, not_fields, op_ctx);

/* If table doesn't have bitset columns, all columns match */
if (!(op_ctx->has_bitset = row_mask.has_bitset)) {
Expand Down
2 changes: 2 additions & 0 deletions test/query/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -2082,6 +2082,8 @@
"fixed_2_src_w_toggle",
"this_w_fixed_src_w_toggle",
"fixed_src_w_this_w_toggle",
"or_chain_3_toggleable_tags",
"or_chain_3_toggleable_components",
"this_from_nothing",
"this",
"this_skip_initial",
Expand Down
116 changes: 116 additions & 0 deletions test/query/src/Toggle.c
Original file line number Diff line number Diff line change
Expand Up @@ -6165,3 +6165,119 @@ void Toggle_toggle_0_src(void) {

ecs_fini(world);
}

static
int32_t toggle_or_match_count(
ecs_world_t *world,
ecs_query_t *q)
{
int32_t count = 0;
ecs_iter_t it = ecs_query_iter(world, q);
while (ecs_query_next(&it)) {
count += it.count;
}
return count;
}

void Toggle_or_chain_3_toggleable_tags(void) {
ecs_world_t *world = ecs_mini();

ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_TAG(world, TagC);

ecs_add_id(world, TagA, EcsCanToggle);
ecs_add_id(world, TagB, EcsCanToggle);
ecs_add_id(world, TagC, EcsCanToggle);

ecs_entity_t e = ecs_new_w_id(world, TagA);
ecs_add(world, e, TagB);
ecs_add(world, e, TagC);

ecs_enable_id(world, e, TagA, false);
ecs_enable_id(world, e, TagB, false);
ecs_enable_id(world, e, TagC, false);

ecs_query_t *q = ecs_query(world, {
.terms = {
{ .id = TagA, .oper = EcsOr },
{ .id = TagB, .oper = EcsOr },
{ .id = TagC }
},
.cache_kind = cache_kind
});
test_assert(q != NULL);

test_int(toggle_or_match_count(world, q), 0);

ecs_enable_id(world, e, TagA, true);
test_int(toggle_or_match_count(world, q), 1);

ecs_enable_id(world, e, TagA, false);
ecs_enable_id(world, e, TagB, true);
test_int(toggle_or_match_count(world, q), 1);

ecs_enable_id(world, e, TagB, false);
ecs_enable_id(world, e, TagC, true);
test_int(toggle_or_match_count(world, q), 1);

ecs_enable_id(world, e, TagA, true);
ecs_enable_id(world, e, TagB, true);
test_int(toggle_or_match_count(world, q), 1);

ecs_query_fini(q);

ecs_fini(world);
}

void Toggle_or_chain_3_toggleable_components(void) {
ecs_world_t *world = ecs_mini();

ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_COMPONENT(world, Mass);

ecs_add_id(world, ecs_id(Position), EcsCanToggle);
ecs_add_id(world, ecs_id(Velocity), EcsCanToggle);
ecs_add_id(world, ecs_id(Mass), EcsCanToggle);

ecs_entity_t e = ecs_new_w(world, Position);
ecs_set(world, e, Position, {10, 20});
ecs_set(world, e, Velocity, {1, 2});
ecs_set(world, e, Mass, {100});

ecs_enable_component(world, e, Position, false);
ecs_enable_component(world, e, Velocity, false);
ecs_enable_component(world, e, Mass, false);

ecs_query_t *q = ecs_query(world, {
.terms = {
{ .id = ecs_id(Position), .oper = EcsOr },
{ .id = ecs_id(Velocity), .oper = EcsOr },
{ .id = ecs_id(Mass) }
},
.cache_kind = cache_kind
});
test_assert(q != NULL);

test_int(toggle_or_match_count(world, q), 0);

ecs_enable_component(world, e, Position, true);
test_int(toggle_or_match_count(world, q), 1);

ecs_enable_component(world, e, Position, false);
ecs_enable_component(world, e, Velocity, true);
test_int(toggle_or_match_count(world, q), 1);

ecs_enable_component(world, e, Velocity, false);
ecs_enable_component(world, e, Mass, true);
test_int(toggle_or_match_count(world, q), 1);

ecs_enable_component(world, e, Position, true);
ecs_enable_component(world, e, Velocity, true);
test_int(toggle_or_match_count(world, q), 1);

ecs_query_fini(q);

ecs_fini(world);
}
Loading
Loading