From 41fb856c4e1162f44b59d7881ef508d64d56bf10 Mon Sep 17 00:00:00 2001 From: Sander Mertens Date: Thu, 24 Aug 2023 01:38:57 -0700 Subject: [PATCH] #861 Add support for setting this variable in queries --- flecs.c | 63 ++++++++++++-- flecs.h | 1 + include/flecs/private/api_types.h | 1 + src/addons/json/serialize.c | 4 +- src/iter.c | 4 + src/query.c | 55 ++++++++++-- test/api/project.json | 6 +- test/api/src/Query.c | 138 ++++++++++++++++++++++++++++++ test/api/src/main.c | 22 ++++- 9 files changed, 274 insertions(+), 20 deletions(-) diff --git a/flecs.c b/flecs.c index 10617d1f7..74eb5cafd 100644 --- a/flecs.c +++ b/flecs.c @@ -13329,6 +13329,10 @@ void ecs_iter_set_var( it->constrained_vars |= flecs_ito(uint64_t, 1 << var_id); + if (it->set_var) { + it->set_var(it); + } + error: return; } @@ -19234,6 +19238,38 @@ const ecs_filter_t* ecs_query_get_filter( return &query->filter; } +static +void flecs_query_set_var( + ecs_iter_t *it) +{ + ecs_check(it->constrained_vars == 1, ECS_INVALID_OPERATION, + "can only set $this variable for queries"); + + ecs_var_t *var = &it->variables[0]; + ecs_table_t *table = var->range.table; + if (!table) { + goto nodata; + } + + ecs_query_iter_t *qit = &it->priv.iter.query; + ecs_query_t *query = qit->query; + ecs_query_table_t *qt = ecs_table_cache_get(&query->cache, table); + if (!qt) { + goto nodata; + } + + qit->node = qt->first; + qit->last = qt->last->next_match; + it->offset = var->range.offset; + it->count = var->range.count; + return; +error: +nodata: + it->priv.iter.query.node = NULL; + it->priv.iter.query.last = NULL; + return; +} + ecs_iter_t ecs_query_iter( const ecs_world_t *stage, ecs_query_t *query) @@ -19282,8 +19318,10 @@ ecs_iter_t ecs_query_iter( .terms = query->filter.terms, .field_count = query->filter.field_count, .table_count = table_count, + .variable_count = 1, .priv.iter.query = it, .next = ecs_query_next, + .set_var = flecs_query_set_var }; flecs_filter_apply_iter_flags(&result, &query->filter); @@ -19318,7 +19356,8 @@ ecs_iter_t ecs_query_iter( } } else { /* Trivial iteration, use arrays from query cache */ - flecs_iter_init(stage, &result, flecs_iter_cache_ptrs); + flecs_iter_init(stage, &result, + flecs_iter_cache_ptrs|flecs_iter_cache_variables); } result.sizes = query->filter.sizes; @@ -19476,15 +19515,20 @@ void flecs_query_populate_trivial( ecs_query_table_match_t *match) {; ecs_table_t *table = match->table; - int32_t count = ecs_table_count(table); + int32_t offset, count; + if (!it->constrained_vars) { + it->offset = offset = 0; + it->count = count = ecs_table_count(table); + } else { + offset = it->offset; + count = it->count; + } it->ids = match->ids; it->sources = match->sources; it->columns = match->columns; it->group_id = match->group_id; it->instance_count = 0; - it->offset = 0; - it->count = count; it->references = ecs_vec_first(&match->refs); if (!it->references) { @@ -19505,15 +19549,16 @@ void flecs_query_populate_trivial( } it->ptrs[i] = ecs_vec_get(&data->columns[column].data, - it->sizes[i], 0); + it->sizes[i], offset); } } it->frame_offset += it->table ? ecs_table_count(it->table) : 0; it->table = table; - it->entities = ecs_vec_first(&data->entities); + it->entities = ecs_vec_get_t(&data->entities, ecs_entity_t, offset); } else { - flecs_iter_populate_data(it->real_world, it, table, 0, count, it->ptrs); + flecs_iter_populate_data( + it->real_world, it, table, offset, count, it->ptrs); } } @@ -51765,7 +51810,7 @@ void flecs_json_serialize_iter_variables(ecs_iter_t *it, ecs_strbuf_t *buf) { int32_t var_count = it->variable_count; int32_t actual_count = 0; - for (int i = 0; i < var_count; i ++) { + for (int i = 1; i < var_count; i ++) { const char *var_name = variable_names[i]; if (flecs_json_skip_variable(var_name)) continue; @@ -51897,7 +51942,7 @@ void flecs_json_serialize_iter_result_variables( int32_t var_count = it->variable_count; int32_t actual_count = 0; - for (int i = 0; i < var_count; i ++) { + for (int i = 1; i < var_count; i ++) { const char *var_name = variable_names[i]; if (flecs_json_skip_variable(var_name)) continue; diff --git a/flecs.h b/flecs.h index 086c18039..c373bf0d0 100644 --- a/flecs.h +++ b/flecs.h @@ -3274,6 +3274,7 @@ struct ecs_iter_t { /* Chained iterators */ ecs_iter_next_action_t next; /* Function to progress iterator */ ecs_iter_action_t callback; /* Callback of system or observer */ + ecs_iter_action_t set_var; /* Invoked after setting variable (optionally set) */ ecs_iter_fini_action_t fini; /* Function to cleanup iterator resources */ ecs_iter_t *chain_it; /* Optional, allows for creating iterator chains */ }; diff --git a/include/flecs/private/api_types.h b/include/flecs/private/api_types.h index ecfb9e26b..20fd68910 100644 --- a/include/flecs/private/api_types.h +++ b/include/flecs/private/api_types.h @@ -302,6 +302,7 @@ struct ecs_iter_t { /* Chained iterators */ ecs_iter_next_action_t next; /* Function to progress iterator */ ecs_iter_action_t callback; /* Callback of system or observer */ + ecs_iter_action_t set_var; /* Invoked after setting variable (optionally set) */ ecs_iter_fini_action_t fini; /* Function to cleanup iterator resources */ ecs_iter_t *chain_it; /* Optional, allows for creating iterator chains */ }; diff --git a/src/addons/json/serialize.c b/src/addons/json/serialize.c index e99ad65da..4374638bb 100644 --- a/src/addons/json/serialize.c +++ b/src/addons/json/serialize.c @@ -1420,7 +1420,7 @@ void flecs_json_serialize_iter_variables(ecs_iter_t *it, ecs_strbuf_t *buf) { int32_t var_count = it->variable_count; int32_t actual_count = 0; - for (int i = 0; i < var_count; i ++) { + for (int i = 1; i < var_count; i ++) { const char *var_name = variable_names[i]; if (flecs_json_skip_variable(var_name)) continue; @@ -1552,7 +1552,7 @@ void flecs_json_serialize_iter_result_variables( int32_t var_count = it->variable_count; int32_t actual_count = 0; - for (int i = 0; i < var_count; i ++) { + for (int i = 1; i < var_count; i ++) { const char *var_name = variable_names[i]; if (flecs_json_skip_variable(var_name)) continue; diff --git a/src/iter.c b/src/iter.c index 28358f89b..a3705da38 100644 --- a/src/iter.c +++ b/src/iter.c @@ -763,6 +763,10 @@ void ecs_iter_set_var( it->constrained_vars |= flecs_ito(uint64_t, 1 << var_id); + if (it->set_var) { + it->set_var(it); + } + error: return; } diff --git a/src/query.c b/src/query.c index 8f3219283..472f3970b 100644 --- a/src/query.c +++ b/src/query.c @@ -2182,6 +2182,38 @@ const ecs_filter_t* ecs_query_get_filter( return &query->filter; } +static +void flecs_query_set_var( + ecs_iter_t *it) +{ + ecs_check(it->constrained_vars == 1, ECS_INVALID_OPERATION, + "can only set $this variable for queries"); + + ecs_var_t *var = &it->variables[0]; + ecs_table_t *table = var->range.table; + if (!table) { + goto nodata; + } + + ecs_query_iter_t *qit = &it->priv.iter.query; + ecs_query_t *query = qit->query; + ecs_query_table_t *qt = ecs_table_cache_get(&query->cache, table); + if (!qt) { + goto nodata; + } + + qit->node = qt->first; + qit->last = qt->last->next_match; + it->offset = var->range.offset; + it->count = var->range.count; + return; +error: +nodata: + it->priv.iter.query.node = NULL; + it->priv.iter.query.last = NULL; + return; +} + ecs_iter_t ecs_query_iter( const ecs_world_t *stage, ecs_query_t *query) @@ -2230,8 +2262,10 @@ ecs_iter_t ecs_query_iter( .terms = query->filter.terms, .field_count = query->filter.field_count, .table_count = table_count, + .variable_count = 1, .priv.iter.query = it, .next = ecs_query_next, + .set_var = flecs_query_set_var }; flecs_filter_apply_iter_flags(&result, &query->filter); @@ -2266,7 +2300,8 @@ ecs_iter_t ecs_query_iter( } } else { /* Trivial iteration, use arrays from query cache */ - flecs_iter_init(stage, &result, flecs_iter_cache_ptrs); + flecs_iter_init(stage, &result, + flecs_iter_cache_ptrs|flecs_iter_cache_variables); } result.sizes = query->filter.sizes; @@ -2424,15 +2459,20 @@ void flecs_query_populate_trivial( ecs_query_table_match_t *match) {; ecs_table_t *table = match->table; - int32_t count = ecs_table_count(table); + int32_t offset, count; + if (!it->constrained_vars) { + it->offset = offset = 0; + it->count = count = ecs_table_count(table); + } else { + offset = it->offset; + count = it->count; + } it->ids = match->ids; it->sources = match->sources; it->columns = match->columns; it->group_id = match->group_id; it->instance_count = 0; - it->offset = 0; - it->count = count; it->references = ecs_vec_first(&match->refs); if (!it->references) { @@ -2453,15 +2493,16 @@ void flecs_query_populate_trivial( } it->ptrs[i] = ecs_vec_get(&data->columns[column].data, - it->sizes[i], 0); + it->sizes[i], offset); } } it->frame_offset += it->table ? ecs_table_count(it->table) : 0; it->table = table; - it->entities = ecs_vec_first(&data->entities); + it->entities = ecs_vec_get_t(&data->entities, ecs_entity_t, offset); } else { - flecs_iter_populate_data(it->real_world, it, table, 0, count, it->ptrs); + flecs_iter_populate_data( + it->real_world, it, table, offset, count, it->ptrs); } } diff --git a/test/api/project.json b/test/api/project.json index 888e94e72..5abcecc80 100644 --- a/test/api/project.json +++ b/test/api/project.json @@ -1657,7 +1657,11 @@ "set_get_context", "set_get_binding_context", "set_get_context_w_free", - "set_get_binding_context_w_free" + "set_get_binding_context_w_free", + "set_this", + "set_this_no_match", + "set_this_is_true", + "set_this_w_wildcard" ] }, { "id": "Iter", diff --git a/test/api/src/Query.c b/test/api/src/Query.c index da42c7619..615db2f39 100644 --- a/test/api/src/Query.c +++ b/test/api/src/Query.c @@ -9431,3 +9431,141 @@ void Query_set_get_binding_context_w_free(void) { ecs_fini(ecs); } + +void Query_set_this(void) { + ecs_world_t* ecs = ecs_init(); + + ECS_COMPONENT(ecs, Position); + + ecs_query_t *q = ecs_query(ecs, { + .filter.terms = { + { .id = ecs_id(Position) } + } + }); + test_assert(q != NULL); + + /* ecs_entity_t e1 = */ ecs_set(ecs, 0, Position, {10, 20}); + ecs_entity_t e2 = ecs_set(ecs, 0, Position, {20, 30}); + /* ecs_entity_t e3 = */ ecs_set(ecs, 0, Position, {30, 40}); + + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + Position *p = ecs_field(&it, Position, 1); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(ecs); +} + +void Query_set_this_no_match(void) { + ecs_world_t* ecs = ecs_init(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_query_t *q = ecs_query(ecs, { + .filter.terms = { + { .id = ecs_id(Position) } + } + }); + test_assert(q != NULL); + + /* ecs_entity_t e1 = */ ecs_set(ecs, 0, Position, {10, 20}); + ecs_entity_t e2 = ecs_set(ecs, 0, Velocity, {20, 30}); + /* ecs_entity_t e3 = */ ecs_set(ecs, 0, Position, {30, 40}); + + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(false, ecs_query_next(&it)); + + ecs_fini(ecs); +} + +void Query_set_this_is_true(void) { + ecs_world_t* ecs = ecs_init(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ecs_query_t *q = ecs_query(ecs, { + .filter.terms = { + { .id = ecs_id(Position) } + } + }); + test_assert(q != NULL); + + /* ecs_entity_t e1 = */ ecs_set(ecs, 0, Position, {10, 20}); + ecs_entity_t e2 = ecs_set(ecs, 0, Position, {20, 30}); + /* ecs_entity_t e3 = */ ecs_set(ecs, 0, Position, {30, 40}); + ecs_entity_t e4 = ecs_set(ecs, 0, Velocity, {20, 30}); + + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + test_bool(true, ecs_iter_is_true(&it)); + + it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e4); + test_bool(false, ecs_iter_is_true(&it)); + + ecs_fini(ecs); +} + +void Query_set_this_w_wildcard(void) { + ecs_world_t* ecs = ecs_init(); + + ECS_COMPONENT(ecs, Position); + ECS_TAG(ecs, Likes); + ECS_TAG(ecs, Apples); + ECS_TAG(ecs, Pears); + + ecs_query_t *q = ecs_query(ecs, { + .filter.terms = { + { .id = ecs_id(Position) }, + { .id = ecs_pair(Likes, EcsWildcard) } + } + }); + test_assert(q != NULL); + + ecs_entity_t e1 = ecs_set(ecs, 0, Position, {10, 20}); + ecs_entity_t e2 = ecs_set(ecs, 0, Position, {20, 30}); + ecs_entity_t e3 = ecs_set(ecs, 0, Position, {30, 40}); + + ecs_add_pair(ecs, e1, Likes, Apples); + ecs_add_pair(ecs, e2, Likes, Apples); + ecs_add_pair(ecs, e3, Likes, Apples); + + ecs_add_pair(ecs, e1, Likes, Pears); + ecs_add_pair(ecs, e2, Likes, Pears); + ecs_add_pair(ecs, e3, Likes, Pears); + + ecs_iter_t it = ecs_query_iter(ecs, q); + ecs_iter_set_var(&it, 0, e2); + + { + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + Position *p = ecs_field(&it, Position, 1); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + test_uint(ecs_pair(Likes, Apples), ecs_field_id(&it, 2)); + } + { + test_bool(true, ecs_query_next(&it)); + test_int(it.count, 1); + test_uint(it.entities[0], e2); + Position *p = ecs_field(&it, Position, 1); + test_int(p[0].x, 20); + test_int(p[0].y, 30); + test_uint(ecs_pair(Likes, Pears), ecs_field_id(&it, 2)); + } + + test_bool(false, ecs_query_next(&it)); + + ecs_fini(ecs); +} diff --git a/test/api/src/main.c b/test/api/src/main.c index e268e03a4..0c58f1fbc 100644 --- a/test/api/src/main.c +++ b/test/api/src/main.c @@ -1595,6 +1595,10 @@ void Query_set_get_context(void); void Query_set_get_binding_context(void); void Query_set_get_context_w_free(void); void Query_set_get_binding_context_w_free(void); +void Query_set_this(void); +void Query_set_this_no_match(void); +void Query_set_this_is_true(void); +void Query_set_this_w_wildcard(void); // Testsuite 'Iter' void Iter_page_iter_0_0(void); @@ -8778,6 +8782,22 @@ bake_test_case Query_testcases[] = { { "set_get_binding_context_w_free", Query_set_get_binding_context_w_free + }, + { + "set_this", + Query_set_this + }, + { + "set_this_no_match", + Query_set_this_no_match + }, + { + "set_this_is_true", + Query_set_this_is_true + }, + { + "set_this_w_wildcard", + Query_set_this_w_wildcard } }; @@ -12896,7 +12916,7 @@ static bake_test_suite suites[] = { "Query", NULL, NULL, - 231, + 235, Query_testcases }, {