@@ -15638,7 +15638,7 @@ void flecs_emit(
1563815638 }
1563915639
1564015640 /* Forward events for Parent component as ChildOf pairs. */
15641- if (id == ecs_id(EcsParent)) {
15641+ if (id == ecs_id(EcsParent) && !table_event ) {
1564215642 ecs_event_desc_t pdesc = *desc;
1564315643
1564415644 pdesc.event = event;
@@ -16111,8 +16111,11 @@ void flecs_uni_observer_register(
1611116111 }
1611216112
1611316113 if (term && (term->trav == EcsChildOf)) {
16114- flecs_register_observer_for_id(world, observable, o,
16115- offsetof(ecs_event_id_record_t, self), ecs_id(EcsParent));
16114+ ecs_assert(o->query != NULL, ECS_INTERNAL_ERROR, NULL);
16115+ if (o->query->flags & EcsQueryTableOnly) {
16116+ flecs_register_observer_for_id(world, observable, o,
16117+ offsetof(ecs_event_id_record_t, self), ecs_id(EcsParent));
16118+ }
1611616119 }
1611716120}
1611816121
@@ -16194,8 +16197,10 @@ void flecs_unregister_observer(
1619416197 }
1619516198
1619616199 if (term && (term->trav == EcsChildOf)) {
16197- flecs_unregister_observer_for_id(world, observable, o,
16198- offsetof(ecs_event_id_record_t, self), ecs_id(EcsParent));
16200+ if (o->query->flags & EcsQueryTableOnly) {
16201+ flecs_unregister_observer_for_id(world, observable, o,
16202+ offsetof(ecs_event_id_record_t, self), ecs_id(EcsParent));
16203+ }
1619916204 }
1620016205}
1620116206
@@ -16946,7 +16951,7 @@ ecs_observer_t* flecs_observer_init(
1694616951 /* Only do optimization when not in sanitized mode. This ensures that the
1694716952 * behavior is consistent between observers with and without queries, as
1694816953 * both paths will be exercised in unit tests. */
16949- // #ifndef FLECS_SANITIZE
16954+ #ifndef FLECS_SANITIZE
1695016955 /* Temporary arrays for dummy query */
1695116956 ecs_term_t terms[FLECS_TERM_COUNT_MAX] = {0};
1695216957 ecs_size_t sizes[FLECS_TERM_COUNT_MAX] = {0};
@@ -16968,8 +16973,10 @@ ecs_observer_t* flecs_observer_init(
1696816973 (dummy_query.flags & EcsQueryMatchOnlySelf) &&
1696916974 !dummy_query.row_fields;
1697016975 if (trivial_observer) {
16971- dummy_query.flags |= desc->query.flags;
16972- query = &dummy_query;
16976+ if (ECS_PAIR_FIRST(dummy_query.terms[0].id) != EcsChildOf) {
16977+ dummy_query.flags |= desc->query.flags;
16978+ query = &dummy_query;
16979+ }
1697316980 } else {
1697416981 /* We're going to create an actual query, so undo the keep_alive
1697516982 * increment of the dummy_query. */
@@ -16981,7 +16988,7 @@ ecs_observer_t* flecs_observer_init(
1698116988 }
1698216989 }
1698316990 }
16984- // #endif
16991+ #endif
1698516992
1698616993 /* Create query */
1698716994 if (!query) {
@@ -74121,6 +74128,7 @@ ecs_query_cache_t* flecs_query_cache_init(
7412174128 ecs_os_memcpy_n(observer_desc.query.terms, q->terms,
7412274129 ecs_term_t, q->term_count);
7412374130 observer_desc.query.expr = NULL; /* Already parsed */
74131+ observer_desc.query.flags |= EcsQueryTableOnly;
7412474132
7412574133 result->observer = flecs_observer_init(world, entity, &observer_desc);
7412674134 if (!result->observer) {
@@ -82979,6 +82987,15 @@ bool flecs_query_trav(
8297982987 */
8298082988
8298182989
82990+ static
82991+ const EcsParent* flecs_query_tree_get_parents(
82992+ ecs_table_range_t range)
82993+ {
82994+ int32_t parent_column = range.table->component_map[ecs_id(EcsParent)];
82995+ ecs_assert(parent_column != -1, ECS_INTERNAL_ERROR, NULL);
82996+ return ecs_table_get_column(range.table, parent_column - 1, range.offset);
82997+ }
82998+
8298282999static
8298383000bool flecs_query_tree_select_tgt(
8298483001 const ecs_query_op_t *op,
@@ -83299,10 +83316,7 @@ bool flecs_query_tree_with(
8329983316 }
8330083317
8330183318 /* Non-fragmenting childof */
83302- int32_t parent_column = range.table->component_map[ecs_id(EcsParent)];
83303- ecs_assert(parent_column != -1, ECS_INTERNAL_ERROR, NULL);
83304- EcsParent *parents = ecs_table_get_column(
83305- range.table, parent_column - 1, range.offset);
83319+ const EcsParent *parents = flecs_query_tree_get_parents(range);
8330683320
8330783321 /* Evaluating a single entity */
8330883322 if (range.count == 1) {
@@ -83413,7 +83427,8 @@ bool flecs_query_tree_with_pre(
8341383427 return false;
8341483428}
8341583429
83416- bool flecs_query_children(
83430+ static
83431+ bool flecs_query_children_select(
8341783432 const ecs_query_op_t *op,
8341883433 bool redo,
8341983434 const ecs_query_run_ctx_t *ctx)
@@ -83456,6 +83471,72 @@ bool flecs_query_children(
8345683471 return true;
8345783472}
8345883473
83474+ static
83475+ bool flecs_query_children_with(
83476+ const ecs_query_op_t *op,
83477+ bool redo,
83478+ const ecs_query_run_ctx_t *ctx)
83479+ {
83480+ if (redo) {
83481+ return false;
83482+ }
83483+
83484+ ecs_table_range_t range = flecs_query_get_range(
83485+ op, &op->src, EcsQuerySrc, ctx);
83486+ if (range.table->flags & EcsTableHasChildOf) {
83487+ return flecs_query_and(op, redo, ctx);
83488+ }
83489+
83490+ if (!(range.table->flags & EcsTableHasParent)) {
83491+ /* If table doesn't have ChildOf or Parent its entities don't have
83492+ * parents. */
83493+ return false;
83494+ }
83495+
83496+ ecs_id_t id = flecs_query_op_get_id(op, ctx);
83497+ ecs_assert(ECS_PAIR_FIRST(id) == EcsChildOf, ECS_INTERNAL_ERROR, NULL);
83498+
83499+ ecs_entity_t tgt = ECS_PAIR_SECOND(id);
83500+ if (tgt == EcsWildcard || tgt == EcsAny) {
83501+ /* Entities in table have parents, so wildcard will always match. */
83502+ return true;
83503+ }
83504+
83505+ /* TODO: if a ChildOf query is constrained to a table with Parent component,
83506+ * only some entities in the table may match the query. TBD on what the
83507+ * behavior should be in this case. For now the query engine only supports
83508+ * constraining the query to a single entity or an entire table. */
83509+ ecs_assert(range.count < 2, ECS_UNSUPPORTED,
83510+ "can only use set_var with single entity for ChildOf($this, parent) terms");
83511+
83512+ if (range.count == 0) {
83513+ /* If matching the entire table, return true. Even though not all
83514+ * entities in the table may match, this lets us add tables with the
83515+ * Parent component to query caches. */
83516+ return true;
83517+ }
83518+
83519+ const EcsParent *parents = flecs_query_tree_get_parents(range);
83520+ if ((uint32_t)parents->value == tgt) {
83521+ return true;
83522+ }
83523+
83524+ return false;
83525+ }
83526+
83527+ bool flecs_query_children(
83528+ const ecs_query_op_t *op,
83529+ bool redo,
83530+ const ecs_query_run_ctx_t *ctx)
83531+ {
83532+ uint64_t written = ctx->written[ctx->op_index];
83533+ if (written & (1ull << op->src.var)) {
83534+ return flecs_query_children_with(op, redo, ctx);
83535+ } else {
83536+ return flecs_query_children_select(op, redo, ctx);
83537+ }
83538+ }
83539+
8345983540bool flecs_query_tree_and(
8346083541 const ecs_query_op_t *op,
8346183542 bool redo,
@@ -85207,6 +85288,24 @@ ecs_trav_up_t* flecs_trav_table_up(
8520785288 }
8520885289 }
8520985290
85291+ if ((rel == ecs_pair(EcsChildOf, EcsWildcard) &&
85292+ (flags & EcsTableHasParent)))
85293+ {
85294+ const EcsParent *p = ecs_table_get_id(
85295+ world, table, ecs_id(EcsParent),
85296+ ECS_RECORD_TO_ROW(src_record->row));
85297+ ecs_assert(p != NULL, ECS_INTERNAL_ERROR, NULL);
85298+
85299+ ecs_trav_up_t *up_parent = flecs_trav_table_up(ctx, a, cache,
85300+ world, p->value, with, rel, cr_with, cr_trav);
85301+ if (up_parent->tr) {
85302+ up->src = up_parent->src;
85303+ up->tr = up_parent->tr;
85304+ up->id = up_parent->id;
85305+ goto found;
85306+ }
85307+ }
85308+
8521085309 ecs_trav_up_t up_pair = {0};
8521185310 int32_t r_column = flecs_trav_type_search(
8521285311 &up_pair, table, cr_trav, &type);
0 commit comments