Files
PixelDefense/engine/libs/flecs/test/addons/src/Pipeline.c

3209 lines
87 KiB
C

#include <addons.h>
static ECS_DECLARE(TagA);
static ECS_DECLARE(TagB);
ECS_COMPONENT_DECLARE(Position);
ECS_COMPONENT_DECLARE(Velocity);
static int sys_a_invoked;
static int sys_b_invoked;
static int sys_c_invoked;
static int sys_d_invoked;
static int sys_e_invoked;
static int sys_f_invoked;
static bool sys_a_real_world;
static bool sys_b_real_world;
static bool sys_c_real_world;
static bool sys_d_real_world;
static bool sys_e_real_world;
static bool sys_f_real_world;
static bool sys_a_world_readonly;
static bool sys_b_world_readonly;
static bool sys_c_world_readonly;
static bool sys_d_world_readonly;
static bool sys_e_world_readonly;
static bool sys_f_world_readonly;
static ecs_ftime_t sys_a_delta_time = 0;
static ecs_ftime_t sys_b_delta_time = 0;
void SysA(ecs_iter_t *it) {
ecs_os_ainc(&sys_a_invoked);
sys_a_real_world = it->world == it->real_world;
sys_a_world_readonly = ecs_stage_is_readonly(it->real_world);
sys_a_delta_time = it->delta_time;
}
void SysB(ecs_iter_t *it) {
test_assert(sys_a_invoked != 0);
ecs_os_ainc(&sys_b_invoked);
sys_b_real_world = it->world == it->real_world;
sys_b_world_readonly = ecs_stage_is_readonly(it->real_world);
sys_b_delta_time = it->delta_time;
}
void SysC(ecs_iter_t *it) {
test_assert(sys_b_invoked != 0);
ecs_os_ainc(&sys_c_invoked);
sys_c_real_world = it->world == it->real_world;
sys_c_world_readonly = ecs_stage_is_readonly(it->real_world);
}
void SysD(ecs_iter_t *it) {
test_assert(sys_c_invoked != 0);
ecs_os_ainc(&sys_d_invoked);
sys_d_real_world = it->world == it->real_world;
sys_d_world_readonly = ecs_stage_is_readonly(it->real_world);
}
void SysE(ecs_iter_t *it) {
test_assert(sys_d_invoked != 0);
ecs_os_ainc(&sys_e_invoked);
sys_e_real_world = it->world == it->real_world;
sys_e_world_readonly = ecs_stage_is_readonly(it->real_world);
}
void SysF(ecs_iter_t *it) {
test_assert(sys_d_invoked != 0);
ecs_os_ainc(&sys_f_invoked);
sys_f_real_world = it->world == it->real_world;
sys_f_world_readonly = ecs_stage_is_readonly(it->real_world);
}
void Pipeline_system_order_same_phase(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysA, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysB, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysC, EcsOnUpdate, Position);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 3);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_system_order_same_phase_after_disable(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysA, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysB, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysC, EcsOnUpdate, Position);
ecs_enable(world, SysB, false);
test_assert( ecs_has_id(world, SysB, EcsDisabled));
ecs_enable(world, SysB, true);
test_assert( !ecs_has_id(world, SysB, EcsDisabled));
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 3);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_system_order_different_phase(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysC, EcsPostUpdate, Position);
ECS_SYSTEM(world, SysB, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysA, EcsPreUpdate, Position);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 3);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_system_order_different_phase_after_disable(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysC, EcsPostUpdate, Position);
ECS_SYSTEM(world, SysB, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysA, EcsPreUpdate, Position);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_enable(world, SysB, false);
test_assert( ecs_has_id(world, SysB, EcsDisabled));
ecs_enable(world, SysB, true);
test_assert( !ecs_has_id(world, SysB, EcsDisabled));
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 3);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_system_order_same_phase_after_activate(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysA, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysB, EcsOnUpdate, Velocity); /* System is deactivated */
ECS_SYSTEM(world, SysC, EcsOnUpdate, Position);
const ecs_world_info_t *stats = ecs_get_world_info(world);
test_assert( ecs_has_id(world, SysB, EcsEmpty));
ecs_add(world, E, Velocity);
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 3);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
test_assert( !ecs_has_id(world, SysB, EcsEmpty));
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_system_order_different_phase_after_activate(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysC, EcsPostUpdate, Position);
ECS_SYSTEM(world, SysB, EcsOnUpdate, Velocity); /* System is deactivated */
ECS_SYSTEM(world, SysA, EcsPreUpdate, Position);
const ecs_world_info_t *stats = ecs_get_world_info(world);
test_assert( ecs_has_id(world, SysB, EcsEmpty));
ecs_add(world, E, Velocity);
ecs_progress(world, 1);
test_assert( !ecs_has_id(world, SysB, EcsEmpty));
test_int(stats->systems_ran_frame, 3);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_system_order_after_new_system_lower_id(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position);
ecs_entity_t Sys = ecs_new(world, 0);
ECS_SYSTEM(world, SysB, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysC, EcsOnUpdate, Position);
const ecs_world_info_t *stats = ecs_get_world_info(world);
/* Create new system with Sys id */
ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.id = Sys, .name = "SysA", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysA
});
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 3);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_system_order_after_new_system_inbetween_id(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysA, EcsOnUpdate, Position);
ecs_entity_t Sys = ecs_new(world, 0);
ECS_SYSTEM(world, SysC, EcsOnUpdate, Position);
const ecs_world_info_t *stats = ecs_get_world_info(world);
/* Create new system with Sys id */
ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.id = Sys, .name = "SysB", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysB
});
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 3);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_system_order_after_new_system_higher_id(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysA, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysB, EcsOnUpdate, Position);
ecs_entity_t Sys = ecs_new(world, 0);
const ecs_world_info_t *stats = ecs_get_world_info(world);
/* Create new system with Sys id */
ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, {.id = Sys, .name = "SysC", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysC
});
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 3);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
static int sys_out_invoked;
static int sys_in_invoked;
static void SysOut(ecs_iter_t *it) {
ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 2);
sys_out_invoked ++;
int i;
for (i = 0; i < it->count; i ++) {
ecs_set(it->world, it->entities[i], Velocity, {10, 20});
}
}
static void SysOutMain(ecs_iter_t *it) {
Velocity *v = ecs_field(it, Velocity, 2);
sys_out_invoked ++;
int i;
for (i = 0; i < it->count; i ++) {
v[i].x = 10;
v[i].y = 20;
}
}
static void SysIn(ecs_iter_t *it) {
ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1);
test_assert(sys_out_invoked != 0);
sys_in_invoked ++;
int i;
for (i = 0; i < it->count; i ++) {
ecs_entity_t e = it->entities[i];
test_assert( ecs_has(it->world, e, Velocity));
const Velocity *v_ptr = ecs_get(it->world, e, Velocity);
test_int(v_ptr->x, 10);
test_int(v_ptr->y, 20);
}
}
static void SysInMain(ecs_iter_t *it) {
Velocity *v = ecs_field(it, Velocity, 1);
ecs_id_t ecs_id(Velocity) = ecs_field_id(it, 1);
test_assert(sys_out_invoked != 0);
sys_in_invoked ++;
int i;
for (i = 0; i < it->count; i ++) {
ecs_entity_t e = it->entities[i];
test_assert( ecs_has(it->world, e, Velocity));
test_int(v[i].x, 10);
test_int(v[i].y, 20);
}
}
void Pipeline_merge_after_staged_out(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysOut, EcsOnUpdate, Position, [out] Velocity());
ECS_SYSTEM(world, SysInMain, EcsOnUpdate, Velocity);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 2);
test_int(stats->merge_count_total, 2);
test_int(stats->pipeline_build_count_total, 2);
test_int(sys_out_invoked, 1);
test_int(sys_in_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 2);
ecs_fini(world);
}
void Pipeline_merge_after_not_out(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysOut, EcsOnUpdate, Position, [out] !Velocity);
ECS_SYSTEM(world, SysInMain, EcsOnUpdate, Velocity);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 2);
test_int(stats->merge_count_total, 2);
test_int(stats->pipeline_build_count_total, 2);
test_int(sys_out_invoked, 1);
test_int(sys_in_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 2);
ecs_fini(world);
}
void Pipeline_no_merge_after_main_out(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position, Velocity);
ECS_SYSTEM(world, SysOutMain, EcsOnUpdate, Position, Velocity);
ECS_SYSTEM(world, SysInMain, EcsOnUpdate, Velocity);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 2);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_out_invoked, 1);
test_int(sys_in_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_merge_after_staged_in_out(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position, Velocity);
/* Requires merge, because getting value in 2nd system cannot access data
* written to stage from first system */
ECS_SYSTEM(world, SysOut, EcsOnUpdate, Position, [out] Velocity());
ECS_SYSTEM(world, SysIn, EcsOnUpdate, [in] Velocity());
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 2);
test_int(stats->merge_count_total, 2);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_out_invoked, 1);
test_int(sys_in_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_merge_after_staged_inout_main_implicit_inout(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysA, EcsOnUpdate, Position, Velocity());
ECS_SYSTEM(world, SysB, EcsOnUpdate, Velocity);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 1);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_merge_after_staged_inout_main_inout(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position, Velocity);
ECS_SYSTEM(world, SysOut, EcsOnUpdate, Position, [inout] Velocity());
ECS_SYSTEM(world, SysIn, EcsOnUpdate, Velocity);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 2);
test_int(stats->merge_count_total, 2);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_out_invoked, 1);
test_int(sys_in_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 1);
ecs_fini(world);
}
void Pipeline_merge_after_staged_out_before_owned(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_COMPONENT(world, Velocity);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysOut, EcsOnUpdate, Position, [out] Velocity());
ECS_SYSTEM(world, SysInMain, EcsOnUpdate, Velocity);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_progress(world, 1);
test_int(stats->systems_ran_frame, 2);
test_int(stats->merge_count_total, 2);
test_int(stats->pipeline_build_count_total, 2);
test_int(sys_out_invoked, 1);
test_int(sys_in_invoked, 1);
ecs_progress(world, 1);
test_int(stats->pipeline_build_count_total, 2);
ecs_fini(world);
}
void Pipeline_switch_pipeline(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysA, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysB, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysC, EcsOnUpdate, Position);
ecs_add(world, ecs_id(SysC), Tag);
ECS_PIPELINE(world, P1, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), !Tag);
ecs_progress(world, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_set_pipeline(world, P1);
ecs_progress(world, 1);
test_int(sys_a_invoked, 2);
test_int(sys_b_invoked, 2);
test_int(sys_c_invoked, 1);
ecs_fini(world);
}
void Pipeline_run_pipeline(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysA, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysB, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysC, EcsPostUpdate, Position);
ecs_add(world, ecs_id(SysC), Tag);
ECS_PIPELINE(world, P1, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), !Tag);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_progress(world, 0);
test_int(stats->systems_ran_frame, 3);
test_int(stats->merge_count_total, 1);
test_int(stats->pipeline_build_count_total, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_run_pipeline(world, P1, 1);
test_int(stats->pipeline_build_count_total, 2);
test_int(sys_a_invoked, 2);
test_int(sys_b_invoked, 2);
test_int(sys_c_invoked, 1);
ecs_fini(world);
}
void Pipeline_get_pipeline_from_stage(void) {
ecs_world_t *world = ecs_init();
ecs_entity_t pipeline = ecs_get_pipeline(world);
test_assert(pipeline != 0);
/* Get default stage */
ecs_world_t *stage = ecs_get_stage(world, 0);
test_assert(stage != NULL);
test_assert(pipeline == ecs_get_pipeline(stage));
ecs_fini(world);
}
void Pipeline_3_systems_3_types(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysA", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysA
});
ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = NULL, .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysB
});
ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysC", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position()",
.callback = SysC
});
test_assert(s1 != 0);
test_assert(s2 != 0);
test_assert(s3 != 0);
ecs_add_id(world, s3, Tag);
ecs_new(world, Position);
ecs_progress(world, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_fini(world);
}
static
void RandomWrite(ecs_iter_t *it) {
ecs_entity_t ecs_id(Position) = ecs_field_id(it, 2);
int i;
for (i = 0; i < it->count; i ++) {
ecs_set(it->world, it->entities[i], Position, {1, 2});
}
}
static
void RandomRead(ecs_iter_t *it) {
ecs_entity_t ecs_id(Position) = ecs_lookup(it->world, "Position");
int i;
for (i = 0; i < it->count; i ++) {
const Position *p = ecs_get(it->world, it->entities[i], Position);
test_assert(p != NULL);
test_int(p->x, 1);
test_int(p->y, 2);
}
}
static
void RandomReadWrite(ecs_iter_t *it) {
ecs_entity_t ecs_id(Position) = ecs_lookup(it->world, "Position");
int i;
for (i = 0; i < it->count; i ++) {
const Position *p = ecs_get(it->world, it->entities[i], Position);
test_assert(p != NULL);
test_int(p->x, 1);
test_int(p->y, 2);
ecs_set(it->world, it->entities[i], Position, {p->x + 1, p->y + 1});
}
}
static
void RandomReadAfterRW(ecs_iter_t *it) {
ecs_entity_t ecs_id(Position) = ecs_lookup(it->world, "Position");
int i;
for (i = 0; i < it->count; i ++) {
const Position *p = ecs_get(it->world, it->entities[i], Position);
test_assert(p != NULL);
test_int(p->x, 2);
test_int(p->y, 3);
}
}
static
void RandomRead_Not(ecs_iter_t *it) {
ecs_entity_t ecs_id(Position) = ecs_field_id(it, 2);
int i;
for (i = 0; i < it->count; i ++) {
test_assert(!ecs_has(it->world, it->entities[i], Position));
const Position *p = ecs_get(it->world, it->entities[i], Position);
test_assert(p == NULL);
}
}
void Pipeline_random_read_after_random_write_out_in(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [out] Position());
ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [in] Position());
ecs_entity_t e = ecs_new(world, Tag);
ecs_progress(world, 1);
test_assert(ecs_has(world, e, Position));
const Position *p = ecs_get(world, e, Position);
test_int(p->x, 1);
test_int(p->y, 2);
ecs_fini(world);
}
void Pipeline_random_read_after_random_write_inout_in(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [inout] Position());
ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [in] Position());
ecs_entity_t e = ecs_new(world, Tag);
ecs_progress(world, 1);
test_assert(ecs_has(world, e, Position));
const Position *p = ecs_get(world, e, Position);
test_int(p->x, 1);
test_int(p->y, 2);
ecs_fini(world);
}
void Pipeline_random_read_after_random_write_out_inout(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [out] Position());
ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [inout] Position());
ecs_entity_t e = ecs_new(world, Tag);
ecs_progress(world, 1);
test_assert(ecs_has(world, e, Position));
const Position *p = ecs_get(world, e, Position);
test_int(p->x, 1);
test_int(p->y, 2);
ecs_fini(world);
}
void Pipeline_random_read_after_random_write_inout_inout(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [inout] Position());
ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [inout] Position());
ecs_entity_t e = ecs_new(world, Tag);
ecs_progress(world, 1);
test_assert(ecs_has(world, e, Position));
const Position *p = ecs_get(world, e, Position);
test_int(p->x, 1);
test_int(p->y, 2);
ecs_fini(world);
}
void Pipeline_random_read_after_random_write_w_not_write(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [out] !Position);
ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [in] Position());
ecs_entity_t e = ecs_new(world, Tag);
ecs_progress(world, 1);
test_assert(ecs_has(world, e, Position));
const Position *p = ecs_get(world, e, Position);
test_int(p->x, 1);
test_int(p->y, 2);
ecs_fini(world);
}
void Pipeline_random_read_after_random_write_w_not_read(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [out] Position());
ECS_SYSTEM(world, RandomRead_Not, EcsOnUpdate, Tag, [in] !Position);
ecs_entity_t e = ecs_new(world, Tag);
ecs_progress(world, 1);
test_assert(ecs_has(world, e, Position));
const Position *p = ecs_get(world, e, Position);
test_int(p->x, 1);
test_int(p->y, 2);
ecs_fini(world);
}
void Pipeline_random_read_after_random_write_w_wildcard(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [out] Position());
ECS_SYSTEM(world, RandomRead, EcsOnUpdate, Tag, [in] *());
ecs_entity_t e = ecs_new(world, Tag);
ecs_progress(world, 1);
test_assert(ecs_has(world, e, Position));
const Position *p = ecs_get(world, e, Position);
test_int(p->x, 1);
test_int(p->y, 2);
ecs_fini(world);
}
void Pipeline_random_in_after_random_inout_after_random_out(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ECS_SYSTEM(world, RandomWrite, EcsOnUpdate, Tag, [out] Position());
ECS_SYSTEM(world, RandomReadWrite, EcsOnUpdate, Tag, [inout] Position());
ECS_SYSTEM(world, RandomReadAfterRW, EcsOnUpdate, Tag, [in] Position());
ecs_entity_t e = ecs_new(world, Tag);
ecs_progress(world, 1);
test_assert(ecs_has(world, e, Position));
const Position *p = ecs_get(world, e, Position);
test_int(p->x, 2);
test_int(p->y, 3);
ecs_fini(world);
}
static
void cb_first(ecs_iter_t *it) {
int32_t *count = it->ctx;
test_int(count[0], 0);
count[0] ++;
}
static
void cb_second(ecs_iter_t *it) {
int32_t *count = it->ctx;
test_int(count[0], 1);
count[0] ++;
}
static
void cb_third(ecs_iter_t *it) {
int32_t *count = it->ctx;
test_int(count[0], 2);
count[0] ++;
}
void Pipeline_system_reverse_order_by_phase_custom_pipeline(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ecs_entity_t PreFrame = ecs_new_w_id(world, EcsPhase);
ecs_entity_t OnFrame = ecs_new_w_id(world, EcsPhase);
ecs_entity_t PostFrame = ecs_new_w_id(world, EcsPhase);
ecs_add_pair(world, OnFrame, EcsDependsOn, PreFrame);
ecs_add_pair(world, PostFrame, EcsDependsOn, OnFrame);
ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag);
int count = 0;
ecs_system_init(world, &(ecs_system_desc_t){
.callback = cb_third,
.ctx = &count,
.entity = ecs_entity(world, {.add = {Tag, ecs_pair(EcsDependsOn, PostFrame)}})
});
ecs_system_init(world, &(ecs_system_desc_t){
.callback = cb_second,
.ctx = &count,
.entity = ecs_entity(world, {.add = {Tag, ecs_pair(EcsDependsOn, OnFrame)}})
});
ecs_system_init(world, &(ecs_system_desc_t){
.callback = cb_first,
.ctx = &count,
.entity = ecs_entity(world, {.add = {Tag, ecs_pair(EcsDependsOn, PreFrame)}})
});
test_int(count, 0);
ecs_set_pipeline(world, P);
ecs_progress(world, 0);
test_int(count, 3);
ecs_fini(world);
}
void Pipeline_stage_write_before_read(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysA", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysA
});
ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysB", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "[out] Position(), Position",
.callback = SysB
});
ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysC", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysC
});
ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag);
ecs_set_pipeline(world, P);
test_assert(s1 != 0);
test_assert(s2 != 0);
test_assert(s3 != 0);
ecs_new(world, Position);
ecs_progress(world, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_pipeline_stats_t stats = {0};
test_bool(ecs_pipeline_stats_get(world,
ecs_get_pipeline(world), &stats), true);
test_int(ecs_vec_count(&stats.systems), 5);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[0], s1);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[1], s2);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[2], 0); /* merge */
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[3], s3);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[4], 0); /* merge */
ecs_pipeline_stats_fini(&stats);
ecs_fini(world);
}
static
void Pipeline_mixed_multithreaded_internal(bool task_threads) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysA", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysA,
.multi_threaded = true
});
ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysB", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysB
});
ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysC", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysC
});
ecs_entity_t s4 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysD", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysD,
.multi_threaded = true
});
ecs_entity_t s5 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysE", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysE,
.multi_threaded = true
});
ecs_entity_t s6 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysF", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysF
});
ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag);
ecs_set_pipeline(world, P);
test_assert(s1 != 0);
test_assert(s2 != 0);
test_assert(s3 != 0);
test_assert(s4 != 0);
test_assert(s5 != 0);
test_assert(s6 != 0);
ecs_new(world, Position);
ecs_new(world, Position);
if (task_threads)
{
ecs_set_task_threads(world, 2);
}
else
{
ecs_set_threads(world, 2);
}
ecs_progress(world, 1);
test_int(sys_a_invoked, 2);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
test_int(sys_d_invoked, 2);
test_int(sys_e_invoked, 2);
test_int(sys_f_invoked, 1);
ecs_pipeline_stats_t stats = {0};
test_bool(ecs_pipeline_stats_get(world,
ecs_get_pipeline(world), &stats), true);
test_int(ecs_vec_count(&stats.systems), 10);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[0], s1);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[1], 0); /* merge */
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[2], s2);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[3], s3);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[4], 0); /* merge */
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[5], s4);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[6], s5);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[7], 0); /* merge */
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[8], s6);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[9], 0); /* merge */
ecs_pipeline_stats_fini(&stats);
ecs_fini(world);
}
void Pipeline_mixed_multithreaded(void) {
Pipeline_mixed_multithreaded_internal(false);
}
void Pipeline_mixed_multithreaded_tasks(void) {
Pipeline_mixed_multithreaded_internal(true);
}
void Pipeline_mixed_staging(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysA", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysA,
.no_readonly = true
});
ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysB", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysB
});
ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysC", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysC
});
ecs_entity_t s4 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysD", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysD,
.no_readonly = true
});
ecs_entity_t s5 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysE", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysE,
.no_readonly = true
});
ecs_entity_t s6 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysF", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysF
});
ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag);
ecs_set_pipeline(world, P);
test_assert(s1 != 0);
test_assert(s2 != 0);
test_assert(s3 != 0);
test_assert(s4 != 0);
test_assert(s5 != 0);
test_assert(s6 != 0);
ecs_new(world, Position);
ecs_new(world, Position);
ecs_progress(world, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
test_int(sys_d_invoked, 1);
test_int(sys_e_invoked, 1);
test_int(sys_f_invoked, 1);
test_int(sys_a_real_world, true);
test_int(sys_b_real_world, false);
test_int(sys_c_real_world, false);
test_int(sys_d_real_world, true);
test_int(sys_e_real_world, true);
test_int(sys_f_real_world, false);
test_int(sys_a_world_readonly, false);
test_int(sys_b_world_readonly, true);
test_int(sys_c_world_readonly, true);
test_int(sys_d_world_readonly, false);
test_int(sys_e_world_readonly, false);
test_int(sys_f_world_readonly, true);
ecs_progress(world, 1);
test_int(sys_a_invoked, 2);
test_int(sys_b_invoked, 2);
test_int(sys_c_invoked, 2);
test_int(sys_d_invoked, 2);
test_int(sys_e_invoked, 2);
test_int(sys_f_invoked, 2);
test_int(sys_a_real_world, true);
test_int(sys_b_real_world, false);
test_int(sys_c_real_world, false);
test_int(sys_d_real_world, true);
test_int(sys_e_real_world, true);
test_int(sys_f_real_world, false);
test_int(sys_a_world_readonly, false);
test_int(sys_b_world_readonly, true);
test_int(sys_c_world_readonly, true);
test_int(sys_d_world_readonly, false);
test_int(sys_e_world_readonly, false);
test_int(sys_f_world_readonly, true);
ecs_pipeline_stats_t stats = {0};
test_bool(ecs_pipeline_stats_get(world,
ecs_get_pipeline(world), &stats), true);
test_int(ecs_vec_count(&stats.systems), 11);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[0], s1);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[1], 0); /* merge */
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[2], s2);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[3], s3);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[4], 0); /* merge */
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[5], s4);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[6], 0); /* merge */
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[7], s5);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[8], 0); /* merge */
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[9], s6);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[10], 0); /* merge */
ecs_pipeline_stats_fini(&stats);
ecs_fini(world);
}
static
void WritePosition(ecs_iter_t *it) {
if (*(bool*)it->ctx) {
ecs_entity_t ecs_id(Position) = ecs_field_id(it, 2);
for (int i = 0; i < it->count; i ++) {
ecs_add(it->world, it->entities[i], Position);
}
}
}
void Pipeline_single_threaded_pipeline_change(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
bool write_position = false;
ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysA", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Tag, !Position",
.callback = SysA
});
ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysB", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Tag, Position",
.callback = SysB
});
ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "WritePosition", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Tag, [out] Position()",
.callback = WritePosition,
.ctx = &write_position
});
ecs_entity_t s4 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysC", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Tag, Position",
.callback = SysC
});
ecs_entity_t s5 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysD", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Tag, !Position",
.callback = SysD
});
/* Initialize to 1 so asserts inside system won't trigger */
sys_a_invoked = 1;
sys_b_invoked = 1;
sys_c_invoked = 1;
sys_d_invoked = 1;
ecs_new(world, Tag);
ecs_new(world, Tag);
test_assert(s1 != 0);
test_assert(s2 != 0);
test_assert(s3 != 0);
test_assert(s4 != 0);
test_assert(s5 != 0);
ecs_progress(world, 0);
ecs_progress(world, 0);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1 + 3);
test_int(sys_b_invoked, 1 + 0);
test_int(sys_c_invoked, 1 + 0);
test_int(sys_d_invoked, 1 + 3);
write_position = true;
ecs_progress(world, 0);
test_int(sys_a_invoked, 1 + 4);
test_int(sys_b_invoked, 1 + 0);
test_int(sys_c_invoked, 1 + 1);
test_int(sys_d_invoked, 1 + 3);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1 + 4);
test_int(sys_b_invoked, 1 + 1);
test_int(sys_c_invoked, 1 + 2);
test_int(sys_d_invoked, 1 + 3);
ecs_fini(world);
}
static
void Pipeline_multi_threaded_pipeline_change_internal(bool task_threads) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
bool write_position = false;
ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysA", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Tag, !Position",
.callback = SysA,
.multi_threaded = true
});
ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysB", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Tag, Position, [out] Position()",
.callback = SysB,
.multi_threaded = true
});
ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "WritePosition", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Tag, [out] Position(), [in] ?Position",
.callback = WritePosition,
.ctx = &write_position,
.multi_threaded = true
});
ecs_entity_t s4 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysC", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Tag, Position",
.callback = SysC,
.multi_threaded = true
});
ecs_entity_t s5 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysD", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Tag, !Position",
.callback = SysD,
.multi_threaded = true
});
if (task_threads) {
ecs_set_task_threads(world, 2);
} else {
ecs_set_threads(world, 2);
}
/* Initialize to 1 so asserts inside system won't trigger */
sys_a_invoked = 1;
sys_b_invoked = 1;
sys_c_invoked = 1;
sys_d_invoked = 1;
ecs_new(world, Tag);
ecs_new(world, Tag);
test_assert(s1 != 0);
test_assert(s2 != 0);
test_assert(s3 != 0);
test_assert(s4 != 0);
test_assert(s5 != 0);
ecs_progress(world, 0);
ecs_progress(world, 0);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1 + 3 * 2);
test_int(sys_b_invoked, 1 + 0 * 2);
test_int(sys_c_invoked, 1 + 0 * 2);
test_int(sys_d_invoked, 1 + 3 * 2);
write_position = true;
ecs_progress(world, 0);
test_int(sys_a_invoked, 1 + 4 * 2);
test_int(sys_b_invoked, 1 + 0 * 2);
test_int(sys_c_invoked, 1 + 1 * 2);
test_int(sys_d_invoked, 1 + 3 * 2);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1 + 4 * 2);
test_int(sys_b_invoked, 1 + 1 * 2);
test_int(sys_c_invoked, 1 + 2 * 2);
test_int(sys_d_invoked, 1 + 3 * 2);
ecs_fini(world);
}
void Pipeline_multi_threaded_pipeline_change(void) {
Pipeline_multi_threaded_pipeline_change_internal(false);
}
void Pipeline_multi_threaded_pipeline_change_tasks(void) {
Pipeline_multi_threaded_pipeline_change_internal(true);
}
void Pipeline_activate_after_add(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
bool write_position = false;
ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "WritePosition", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Tag, [out] Position()",
.callback = WritePosition,
.ctx = &write_position
});
ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysA", .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysA
});
ecs_new(world, Tag);
test_assert(s1 != 0);
test_assert(s2 != 0);
ecs_progress(world, 0);
ecs_progress(world, 0);
ecs_progress(world, 0);
test_int(sys_a_invoked, 0);
write_position = true;
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
ecs_fini(world);
}
static ecs_query_t *q_result;
static
void CreateQuery(ecs_iter_t *it) {
test_assert(it->real_world == it->world);
q_result = ecs_query_new(it->world, "Position");
}
void Pipeline_no_staging_system_create_query(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
ecs_entity_t s = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "CreateQuery", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.callback = CreateQuery,
.no_readonly = true
});
ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag);
ecs_set_pipeline(world, P);
test_assert(s != 0);
ecs_progress(world, 1.0);
test_assert(q_result != NULL);
ecs_pipeline_stats_t stats = {0};
test_bool(ecs_pipeline_stats_get(world,
ecs_get_pipeline(world), &stats), true);
test_int(ecs_vec_count(&stats.systems), 2);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[0], s);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[1], 0); /* merge */
ecs_pipeline_stats_fini(&stats);
ecs_fini(world);
}
static int set_singleton_invoked = 0;
static int match_singleton_invoked = 0;
static int match_all_invoked = 0;
static void set_singleton(ecs_iter_t *it) {
ecs_singleton_add(it->world, TagB);
set_singleton_invoked ++;
}
static void match_singleton(ecs_iter_t *it) {
match_singleton_invoked ++;
}
static void match_all(ecs_iter_t *it) {
match_all_invoked ++;
}
void Pipeline_match_all_after_pipeline_rebuild(void) {
ecs_world_t *world = ecs_init();
ECS_TAG_DEFINE(world, TagA);
ECS_TAG_DEFINE(world, TagB);
ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "[out] TagB()",
.callback = set_singleton
});
ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "TagB",
.callback = match_singleton
});
ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .add = {ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "?TagA",
.callback = match_all
});
ecs_progress(world, 0);
test_int(set_singleton_invoked, 1);
test_int(match_singleton_invoked, 1);
test_assert(match_all_invoked >= 1);
ecs_fini(world);
}
void Pipeline_empty_pipeline(void) {
ecs_world_t *world = ecs_mini();
ECS_IMPORT(world, FlecsPipeline);
const ecs_world_info_t *info = ecs_get_world_info(world);
test_int(info->frame_count_total, 0);
ecs_progress(world, 0);
test_int(info->frame_count_total, 1);
ecs_fini(world);
}
void Pipeline_custom_pipeline_w_system_macro(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
ECS_ENTITY(world, E, Position);
ECS_PIPELINE(world, P, flecs.system.System, Tag);
ECS_SYSTEM(world, SysA, Tag, Position);
ECS_SYSTEM(world, SysB, Tag, Position);
ECS_SYSTEM(world, SysC, Tag, Position);
ecs_set_pipeline(world, P);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
ecs_fini(world);
}
void Pipeline_pipeline_w_short_notation(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ecs_entity_t p = ecs_pipeline(world, {
.query.filter.expr = "flecs.system.System, Tag"
});
test_assert(p != 0);
ecs_fini(world);
}
void Pipeline_stack_allocator_after_progress(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysA, EcsOnUpdate, Position);
ecs_filter_t *f = ecs_filter(world, {
.terms = {{ ecs_id(Position) }}
});
ecs_iter_t it = ecs_filter_iter(world, f);
ecs_stack_cursor_t cursor = *it.priv.cache.stack_cursor;
ecs_iter_fini(&it);
ecs_progress(world, 1);
test_int(sys_a_invoked, 1);
it = ecs_filter_iter(world, f);
test_assert(it.priv.cache.stack_cursor->page == cursor.page);
test_assert(it.priv.cache.stack_cursor->sp == cursor.sp);
ecs_iter_fini(&it);
ecs_filter_fini(f);
ecs_fini(world);
}
void Pipeline_stack_allocator_after_progress_w_pipeline_change(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_ENTITY(world, E, Position);
ECS_SYSTEM(world, SysA, EcsOnUpdate, Position);
ECS_SYSTEM(world, SysB, EcsOnUpdate, Position);
ecs_filter_t *f = ecs_filter(world, {
.terms = {{ ecs_id(Position) }}
});
ecs_iter_t it = ecs_filter_iter(world, f);
ecs_stack_cursor_t cursor = *it.priv.cache.stack_cursor;
ecs_iter_fini(&it);
ecs_progress(world, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
it = ecs_filter_iter(world, f);
test_assert(it.priv.cache.stack_cursor->page == cursor.page);
test_assert(it.priv.cache.stack_cursor->sp == cursor.sp);
ecs_iter_fini(&it);
ecs_enable(world, SysB, false);
ecs_progress(world, 1);
test_int(sys_a_invoked, 2);
test_int(sys_b_invoked, 1);
it = ecs_filter_iter(world, f);
test_assert(it.priv.cache.stack_cursor->page == cursor.page);
test_assert(it.priv.cache.stack_cursor->sp == cursor.sp);
ecs_iter_fini(&it);
ecs_filter_fini(f);
ecs_fini(world);
}
static
void Sys_w_MainWorldIter(ecs_iter_t *it) {
ecs_id_t ecs_id(Position) = ecs_field_id(it, 1);
ecs_filter_t *f = ecs_filter(it->real_world, {
.terms = {{ ecs_id(Position) }}
});
ecs_iter_t fit = ecs_filter_iter(it->real_world, f);
test_bool(true, ecs_filter_next(&fit));
test_int(1, fit.count);
test_bool(false, ecs_filter_next(&fit));
ecs_filter_fini(f);
}
static
void Pipeline_iter_from_world_in_singlethread_system_multitead_app_internal(bool task_threads) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ecs_new(world, Position);
ECS_SYSTEM(world, Sys_w_MainWorldIter, EcsOnUpdate, Position());
if (task_threads)
{
ecs_set_task_threads(world, 2);
}
else
{
ecs_set_threads(world, 2);
}
ecs_progress(world, 0);
ecs_fini(world);
}
void Pipeline_iter_from_world_in_singlethread_system_multitead_app(void) {
Pipeline_iter_from_world_in_singlethread_system_multitead_app_internal(false);
}
void Pipeline_iter_from_world_in_singlethread_system_multitead_app_tasks(void) {
Pipeline_iter_from_world_in_singlethread_system_multitead_app_internal(true);
}
static int staging_system_invoked = 0;
static void StagingSystem(ecs_iter_t *it) {
test_assert( ecs_stage_is_readonly(it->real_world));
staging_system_invoked ++;
}
static int no_staging_system_invoked = 0;
static void NoStagingSystem(ecs_iter_t *it) {
test_assert( !ecs_stage_is_readonly(it->real_world));
no_staging_system_invoked ++;
}
void Pipeline_no_staging_after_inactive_system(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ECS_TAG(world, Tag);
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.query.filter.terms = {{ Tag, .oper = EcsNot }},
.callback = StagingSystem
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.query.filter.terms = {{ ecs_id(Position) }, { Tag, .oper = EcsNot }},
.callback = StagingSystem
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.callback = NoStagingSystem,
.no_readonly = true
});
ecs_progress(world, 0);
test_assert(staging_system_invoked != 0);
test_int(no_staging_system_invoked, 1);
staging_system_invoked = 0;
no_staging_system_invoked = 0;
ecs_progress(world, 0);
test_assert(staging_system_invoked != 0);
test_int(no_staging_system_invoked, 1);
ecs_fini(world);
}
static ecs_entity_t create_position_e = 0;
static ecs_entity_t create_velocity_e = 0;
static int no_staging_create_position_invoked = 0;
static int no_staging_create_velocity_invoked = 0;
static void NoStagingSystemCreatePosition(ecs_iter_t *it) {
ecs_defer_end(it->world);
create_position_e = ecs_new_id(it->world);
ecs_set(it->world, create_position_e, Position, {0, 0});
ecs_filter_t *f = ecs_filter(it->world, {
.terms = {{ ecs_id(Position) }}
});
ecs_iter_t fit = ecs_filter_iter(it->world, f);
test_bool(true, ecs_filter_next(&fit));
test_int(fit.count, 1);
test_uint(fit.entities[0], create_position_e);
test_bool(false, ecs_filter_next(&fit));
ecs_filter_fini(f);
ecs_defer_begin(it->world);
no_staging_create_position_invoked ++;
}
static void NoStagingSystemCreateVelocity(ecs_iter_t *it) {
ecs_defer_end(it->world);
create_velocity_e = ecs_new_id(it->world);
ecs_set(it->world, create_velocity_e, Velocity, {0, 0});
ecs_filter_t *f = ecs_filter(it->world, {
.terms = {{ ecs_id(Velocity) }}
});
ecs_iter_t fit = ecs_filter_iter(it->world, f);
test_bool(true, ecs_filter_next(&fit));
test_int(fit.count, 1);
test_uint(fit.entities[0], create_velocity_e);
test_bool(false, ecs_filter_next(&fit));
ecs_filter_fini(f);
ecs_defer_begin(it->world);
no_staging_create_velocity_invoked ++;
}
static int32_t read_position_invoked = 0;
static void ReadPosition(ecs_iter_t *it) {
ecs_os_ainc(&read_position_invoked);
}
static int32_t read_velocity_invoked = 0;
static void ReadVelocity(ecs_iter_t *it) {
ecs_os_ainc(&read_velocity_invoked);
}
void Pipeline_inactive_system_after_no_staging_system_no_defer_w_filter(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT_DEFINE(world, Position);
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.callback = NoStagingSystemCreatePosition,
.no_readonly = true
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.query.filter.terms = {{ ecs_id(Position) }},
.callback = ReadPosition
});
ecs_progress(world, 0);
test_int(no_staging_create_position_invoked, 1);
test_int(read_position_invoked, 1);
test_assert(create_position_e != 0);
test_assert(ecs_has(world, create_position_e, Position));
ecs_delete(world, create_position_e);
ecs_progress(world, 0);
test_int(no_staging_create_position_invoked, 2);
test_int(read_position_invoked, 2);
ecs_fini(world);
}
void Pipeline_inactive_system_after_no_staging_system_no_defer_w_filter_w_no_staging_at_end(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT_DEFINE(world, Position);
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.callback = NoStagingSystemCreatePosition,
.no_readonly = true
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.query.filter.terms = {{ ecs_id(Position) }},
.callback = ReadPosition
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.callback = SysA,
.no_readonly = true
});
ecs_progress(world, 0);
test_int(no_staging_create_position_invoked, 1);
test_int(read_position_invoked, 1);
test_assert(create_position_e != 0);
test_assert(ecs_has(world, create_position_e, Position));
ecs_delete(world, create_position_e);
ecs_progress(world, 0);
test_int(no_staging_create_position_invoked, 2);
test_int(read_position_invoked, 2);
ecs_fini(world);
}
void Pipeline_inactive_system_after_2_no_staging_system_no_defer_w_filter(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT_DEFINE(world, Position);
ECS_COMPONENT_DEFINE(world, Velocity);
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.callback = NoStagingSystemCreatePosition,
.no_readonly = true
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.callback = NoStagingSystemCreateVelocity,
.no_readonly = true
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.query.filter.terms = {{ ecs_id(Position) }},
.callback = ReadPosition
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.query.filter.terms = {{ ecs_id(Velocity) }},
.callback = ReadVelocity
});
ecs_progress(world, 0);
test_int(no_staging_create_position_invoked, 1);
test_int(no_staging_create_velocity_invoked, 1);
test_int(read_position_invoked, 1);
test_int(read_velocity_invoked, 1);
test_assert(create_position_e != 0);
test_assert(ecs_has(world, create_position_e, Position));
ecs_delete(world, create_position_e);
test_assert(create_velocity_e != 0);
test_assert(ecs_has(world, create_velocity_e, Velocity));
ecs_delete(world, create_velocity_e);
ecs_progress(world, 0);
test_int(no_staging_create_position_invoked, 2);
test_int(no_staging_create_velocity_invoked, 2);
test_int(read_position_invoked, 2);
test_int(read_velocity_invoked, 2);
ecs_fini(world);
}
static
void Pipeline_inactive_multithread_system_after_no_staging_system_no_defer_internal(bool task_threads) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT_DEFINE(world, Position);
ECS_COMPONENT_DEFINE(world, Velocity);
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.callback = NoStagingSystemCreatePosition,
.no_readonly = true
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.query.filter.terms = {{ ecs_id(Position) }},
.callback = ReadPosition,
.multi_threaded = true
});
if (task_threads)
{
ecs_set_task_threads(world, 2);
}
else
{
ecs_set_threads(world, 2);
}
ecs_progress(world, 0);
test_int(no_staging_create_position_invoked, 1);
test_int(read_position_invoked, 1);
test_assert(create_position_e != 0);
test_assert(ecs_has(world, create_position_e, Position));
ecs_delete(world, create_position_e);
ecs_progress(world, 0);
test_int(no_staging_create_position_invoked, 2);
test_int(read_position_invoked, 2);
ecs_fini(world);
}
void Pipeline_inactive_multithread_system_after_no_staging_system_no_defer(void) {
Pipeline_inactive_multithread_system_after_no_staging_system_no_defer_internal(false);
}
void Pipeline_inactive_multithread_tasks_system_after_no_staging_system_no_defer(void) {
Pipeline_inactive_multithread_system_after_no_staging_system_no_defer_internal(true);
}
static
void Pipeline_inactive_multithread_system_after_no_staging_system_no_defer_w_no_staging_at_end_internal(bool task_threads) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT_DEFINE(world, Position);
ECS_COMPONENT_DEFINE(world, Velocity);
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.callback = NoStagingSystemCreatePosition,
.no_readonly = true
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.query.filter.terms = {{ ecs_id(Position) }},
.callback = ReadPosition,
.multi_threaded = true
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate )}
}),
.callback = ReadPosition,
.no_readonly = true
});
if (task_threads)
{
ecs_set_task_threads(world, 2);
}
else
{
ecs_set_threads(world, 2);
}
ecs_progress(world, 0);
test_int(no_staging_create_position_invoked, 1);
test_int(read_position_invoked, 2);
test_assert(create_position_e != 0);
test_assert(ecs_has(world, create_position_e, Position));
ecs_delete(world, create_position_e);
ecs_progress(world, 0);
test_int(no_staging_create_position_invoked, 2);
test_int(read_position_invoked, 4);
ecs_fini(world);
}
void Pipeline_inactive_multithread_system_after_no_staging_system_no_defer_w_no_staging_at_end(void) {
Pipeline_inactive_multithread_system_after_no_staging_system_no_defer_w_no_staging_at_end_internal(false);
}
void Pipeline_inactive_multithread_tasks_system_after_no_staging_system_no_defer_w_no_staging_at_end(void) {
Pipeline_inactive_multithread_system_after_no_staging_system_no_defer_w_no_staging_at_end_internal(true);
}
static int add_id_invoked = 0;
static int foo_system_invoked = 0;
static void AddId(ecs_iter_t *it) {
ecs_world_t *world = it->world;
ecs_id_t id = ecs_field_id(it, 1);
int i;
for (i = 0; i < it->count; i++) {
ecs_add_id(world, it->entities[i], id);
}
add_id_invoked ++;
}
static void FooSystem(ecs_iter_t *it) {
foo_system_invoked ++;
}
static
void Pipeline_multi_threaded_pipeline_change_w_only_singlethreaded_internal(bool task_threads) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
ECS_SYSTEM(world, FooSystem, EcsOnUpdate, Position);
ECS_SYSTEM(world, AddId, EcsOnUpdate, !Position, Tag);
if (task_threads)
{
ecs_set_task_threads(world, 2);
}
else
{
ecs_set_threads(world, 2);
}
ecs_entity_t e = ecs_new(world, Tag);
ecs_progress(world, 0);
test_assert(ecs_has(world, e, Position));
test_int(foo_system_invoked, 0);
test_int(add_id_invoked, 1);
ecs_fini(world);
}
void Pipeline_multi_threaded_pipeline_change_w_only_singlethreaded(void) {
Pipeline_multi_threaded_pipeline_change_w_only_singlethreaded_internal(false);
}
void Pipeline_multi_threaded_tasks_pipeline_change_w_only_singlethreaded(void) {
Pipeline_multi_threaded_pipeline_change_w_only_singlethreaded_internal(true);
}
static void SetPosition(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
for (int i = 0; i < it->count; i ++) {
p[i].x = 10;
p[i].y = 20;
}
}
void Pipeline_sync_after_not_out_for_out(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
ECS_SYSTEM(world, AddId, EcsOnUpdate, [out] !Position, Tag);
ECS_SYSTEM(world, SetPosition, EcsOnUpdate, [out] Position);
ecs_entity_t e = ecs_new(world, Tag);
const ecs_world_info_t *wi = ecs_get_world_info(world);
ecs_progress(world, 0);
test_int(wi->merge_count_total, 2);
const Position *p = ecs_get(world, e, Position);
test_assert(p != NULL);
test_int(p->x, 10);
test_int(p->y, 20);
ecs_fini(world);
}
void Pipeline_pair_wildcard_read_after_staged_write(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_TAG(world, Rel);
ECS_COMPONENT(world, Position);
ECS_SYSTEM(world, AddId, EcsOnUpdate, [out] !(Rel, Position), Tag);
ECS_SYSTEM(world, SysA, EcsOnUpdate, [in] (Rel, *));
ecs_new(world, Tag);
const ecs_world_info_t *wi = ecs_get_world_info(world);
ecs_progress(world, 0);
test_int(wi->merge_count_total, 2);
test_int(add_id_invoked, 1);
test_int(sys_a_invoked, 1);
ecs_fini(world);
}
static int add_pair_invoked = 0;
static void AddPair(ecs_iter_t *it) {
ecs_world_t *world = it->world;
ecs_id_t id = ecs_field_id(it, 1);
int i;
for (i = 0; i < it->count; i++) {
ecs_add_pair(world, it->entities[i],
ECS_PAIR_FIRST(id), ecs_id(Position));
}
add_pair_invoked ++;
}
void Pipeline_pair_read_after_staged_wildcard_write(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_TAG(world, Rel);
ECS_COMPONENT_DEFINE(world, Position);
ECS_SYSTEM(world, AddPair, EcsOnUpdate, [out] !(Rel, *), Tag);
ECS_SYSTEM(world, SysA, EcsOnUpdate, [in] (Rel, Position));
ecs_new(world, Tag);
const ecs_world_info_t *wi = ecs_get_world_info(world);
ecs_progress(world, 0);
test_int(wi->merge_count_total, 2);
test_int(add_pair_invoked, 1);
test_int(sys_a_invoked, 1);
ecs_fini(world);
}
void Pipeline_no_sync_after_pair_wildcard_read_after_unmatching_staged_write(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_TAG(world, Rel);
ECS_TAG(world, Rel2);
ECS_COMPONENT_DEFINE(world, Position);
ECS_SYSTEM(world, AddPair, EcsOnUpdate, [out] !(Rel2, Position), Tag);
ECS_SYSTEM(world, SysA, EcsOnUpdate, [in] (Rel, *));
ecs_new(world, Tag);
const ecs_world_info_t *wi = ecs_get_world_info(world);
ecs_progress(world, 0);
test_int(wi->merge_count_total, 1);
test_int(add_pair_invoked, 1);
test_int(sys_a_invoked, 0);
test_int(1, ecs_count_id(world, ecs_pair(Rel2, ecs_id(Position))));
ecs_fini(world);
}
void Pipeline_no_merge_after_from_nothing_w_default_inout(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
ECS_SYSTEM(world, SysA, EcsOnUpdate, Tag, Position());
ECS_SYSTEM(world, SysB, EcsOnUpdate, Tag, Position);
ecs_new(world, Tag);
const ecs_world_info_t *wi = ecs_get_world_info(world);
ecs_progress(world, 0);
test_int(wi->merge_count_total, 1);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 0);
ecs_fini(world);
}
static int sys_add_tag_invoked = 0;
static int sys_no_readonly_invoked = 0;
static void sys_add_tag(ecs_iter_t *it) {
ecs_new(it->world, TagA);
ecs_new(it->world, TagB);
sys_add_tag_invoked ++;
test_assert(sys_a_invoked == 0);
}
static void sys_no_readonly(ecs_iter_t *it) {
test_assert(sys_a_invoked == 1);
test_assert(it->world == it->real_world);
test_assert(!ecs_stage_is_readonly(it->real_world));
sys_no_readonly_invoked ++;
}
void Pipeline_on_merge_activate_system_before_merge(void) {
ecs_world_t *world = ecs_init();
ECS_TAG_DEFINE(world, TagA);
ECS_TAG_DEFINE(world, TagB);
// system is annotated with TagA but writes both TagA, TagB
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}),
.query.filter.terms = {{ TagA, .inout = EcsOut, .src.flags = EcsIsEntity }},
.callback = sys_add_tag
});
// no merge inserted between systems, but system activates after merge
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}),
.query.filter.terms = {{ TagB, .inout = EcsIn }},
.callback = SysA
});
// read TagA, causes insertion of merge
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}),
.query.filter.terms = {{ TagA, .inout = EcsIn }},
.no_readonly = true,
.callback = sys_no_readonly
});
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_add_tag_invoked, 1);
test_int(sys_no_readonly_invoked, 1);
test_int(ecs_count(world, TagA), 1);
test_int(ecs_count(world, TagB), 1);
ecs_fini(world);
}
void Pipeline_disable_phase(void) {
ecs_world_t *world = ecs_init();
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}),
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsPostUpdate) }}),
.callback = SysB
});
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
ecs_enable(world, EcsOnUpdate, false);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 2);
ecs_enable(world, EcsOnUpdate, true);
ecs_enable(world, EcsPostUpdate, false);
ecs_progress(world, 0);
test_int(sys_a_invoked, 2);
test_int(sys_b_invoked, 2);
ecs_enable(world, EcsPostUpdate, true);
ecs_progress(world, 0);
test_int(sys_a_invoked, 3);
test_int(sys_b_invoked, 3);
ecs_fini(world);
}
void Pipeline_disable_parent(void) {
ecs_world_t *world = ecs_init();
ecs_entity_t p1 = ecs_new_id(world);
ecs_entity_t p2 = ecs_new_id(world);
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_childof(p1), ecs_dependson(EcsOnUpdate) }}),
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_childof(p2), ecs_dependson(EcsPostUpdate) }}),
.callback = SysB
});
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
ecs_enable(world, p1, false);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 2);
ecs_enable(world, p1, true);
ecs_enable(world, p2, false);
ecs_progress(world, 0);
test_int(sys_a_invoked, 2);
test_int(sys_b_invoked, 2);
ecs_enable(world, p2, true);
ecs_progress(world, 0);
test_int(sys_a_invoked, 3);
test_int(sys_b_invoked, 3);
ecs_fini(world);
}
static int no_staging_add_position_invoked = 0;
static void NoReadonlyAddPosition(ecs_iter_t *it) {
test_assert(it->world == it->real_world);
no_staging_add_position_invoked ++;
ecs_entity_t e = ecs_new_id(it->world);
ecs_add(it->world, e, Position);
}
static
void Pipeline_multi_threaded_no_staging_w_add_after_read_internal(bool task_threads) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT_DEFINE(world, Position);
if (task_threads)
{
ecs_set_task_threads(world, 2);
}
else
{
ecs_set_threads(world, 2);
}
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}),
.query.filter = { .terms = {{ ecs_id(Position) }}},
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}),
.callback = NoReadonlyAddPosition,
.no_readonly = true
});
ecs_progress(world, 0);
test_int(sys_a_invoked, 0);
test_int(no_staging_add_position_invoked, 1);
test_int(ecs_count(world, Position), 1);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(no_staging_add_position_invoked, 2);
test_int(ecs_count(world, Position), 2);
ecs_fini(world);
}
void Pipeline_multi_threaded_no_staging_w_add_after_read(void) {
Pipeline_multi_threaded_no_staging_w_add_after_read_internal(false);
}
void Pipeline_multi_threaded_tasks_no_staging_w_add_after_read(void) {
Pipeline_multi_threaded_no_staging_w_add_after_read_internal(true);
}
void Pipeline_1_startup_system(void) {
ecs_world_t *world = ecs_init();
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}),
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}),
.callback = SysB
});
test_int(stats->merge_count_total, 0);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(stats->merge_count_total, 2);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 2);
test_int(stats->merge_count_total, 3);
test_assert(sys_a_delta_time == 0);
ecs_fini(world);
}
void Pipeline_2_startup_systems(void) {
ecs_world_t *world = ecs_init();
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}),
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}),
.callback = SysB
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}),
.callback = SysC
});
test_int(stats->merge_count_total, 0);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
test_int(stats->merge_count_total, 2);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 2);
test_int(stats->merge_count_total, 3);
test_assert(sys_a_delta_time == 0);
test_assert(sys_b_delta_time == 0);
ecs_fini(world);
}
void Pipeline_2_startup_phases(void) {
ecs_world_t *world = ecs_init();
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_entity_t AfterStart = ecs_new_w_id(world, EcsPhase);
ecs_add_pair(world, AfterStart, EcsDependsOn, EcsOnStart);
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(AfterStart) }}),
.callback = SysB
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}),
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}),
.callback = SysC
});
test_int(stats->merge_count_total, 0);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
test_int(stats->merge_count_total, 2);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 2);
test_int(stats->merge_count_total, 3);
test_assert(sys_a_delta_time == 0);
test_assert(sys_b_delta_time == 0);
ecs_fini(world);
}
void Pipeline_2_startup_systems_w_merge(void) {
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
ecs_new(world, Position);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}),
.query.filter = { .terms = {{ ecs_id(Position), .src.flags = EcsIsEntity, .inout = EcsOut }}},
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnStart) }}),
.query.filter = { .terms = {{ ecs_id(Position) }}},
.callback = SysB
});
ecs_system(world, {
.entity = ecs_entity(world, { .add = { ecs_dependson(EcsOnUpdate) }}),
.callback = SysC
});
test_int(stats->merge_count_total, 0);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
test_int(stats->merge_count_total, 3);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 2);
test_int(stats->merge_count_total, 4);
ecs_fini(world);
}
void Pipeline_inactive_last_system_merge_count(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_TAG(world, TagC);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ TagA },
{ TagB, .src.flags = EcsIsEntity, .inout = EcsOut }
},
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ TagB },
{ TagC, .src.flags = EcsIsEntity, .inout = EcsOut }
},
.callback = SysB
});
test_int(stats->merge_count_total, 0);
ecs_new(world, TagA);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 0);
test_int(stats->merge_count_total, 1);
ecs_fini(world);
}
void Pipeline_inactive_middle_system_merge_count(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_TAG(world, TagC);
ECS_TAG(world, TagD);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ TagA },
{ TagB, .src.flags = EcsIsEntity, .inout = EcsOut }
},
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ TagB },
{ TagC, .src.flags = EcsIsEntity, .inout = EcsOut }
},
.callback = SysC
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ TagC },
{ TagD, .src.flags = EcsIsEntity, .inout = EcsOut }
},
.callback = SysD
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ TagD }
},
.callback = SysB
});
test_int(stats->merge_count_total, 0);
ecs_new(world, TagA);
ecs_new(world, TagD);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 0);
test_int(sys_d_invoked, 0);
test_int(stats->merge_count_total, 2);
ecs_fini(world);
}
static
void CreateEntity(ecs_iter_t *it) {
ecs_id_t tag = ecs_field_id(it, 1);
ecs_new_w_id(it->world, tag);
}
void Pipeline_last_no_readonly_system_merge_count(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, TagA);
ECS_TAG(world, TagB);
ECS_TAG(world, TagC);
ECS_TAG(world, TagD);
const ecs_world_info_t *stats = ecs_get_world_info(world);
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ TagA },
{ TagB, .src.flags = EcsIsEntity, .inout = EcsOut }
},
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { ecs_dependson(EcsOnUpdate) }
}),
.query.filter.terms = {
{ TagD }
},
.callback = CreateEntity,
.no_readonly = true
});
test_int(stats->merge_count_total, 0);
ecs_new(world, TagA);
ecs_new(world, TagD);
test_int(1, ecs_count(world, TagD));
ecs_progress(world, 0);
test_int(2, ecs_count(world, TagD));
test_int(stats->merge_count_total, 1);
ecs_fini(world);
}
void Pipeline_2_pipelines_1_system(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Phase1);
ECS_TAG(world, Phase2);
ecs_entity_t p1 = ecs_pipeline(world, {
.query = {
.filter.terms = {
{ .id = EcsSystem },
{ .id = Phase1 }
}
}
});
ecs_entity_t p2 = ecs_pipeline(world, {
.query = {
.filter.terms = {
{ .id = EcsSystem },
{ .id = Phase2 }
}
}
});
ecs_system(world, {
.entity = ecs_entity(world, {
.add = { Phase1 }
}),
.callback = SysA
});
ecs_progress(world, 0);
test_int(sys_a_invoked, 0);
ecs_set_pipeline(world, p1);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
ecs_set_pipeline(world, p2);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
ecs_fini(world);
}
void Pipeline_builtin_pipeline_w_self_system_term(void) {
ecs_world_t *world = ecs_init();
ecs_entity_t pipeline = ecs_get_pipeline(world);
test_assert(pipeline != 0);
const EcsPoly *p = ecs_get_pair(world, pipeline, EcsPoly, EcsQuery);
test_assert(p != NULL);
test_assert(ecs_poly_is(p->poly, ecs_query_t));
ecs_query_t *q = p->poly;
const ecs_filter_t *f = ecs_query_get_filter(q);
test_assert(f != NULL);
test_assert((f->terms[0].src.flags & EcsTraverseFlags) == EcsSelf);
ecs_fini(world);
}
void Pipeline_custom_pipeline_w_self_system_term(void) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ecs_entity_t pipeline = ecs_pipeline(world, {
.query.filter.terms = {
{ EcsSystem },
{ Tag }
}
});
test_assert(pipeline != 0);
const EcsPoly *p = ecs_get_pair(world, pipeline, EcsPoly, EcsQuery);
test_assert(p != NULL);
test_assert(ecs_poly_is(p->poly, ecs_query_t));
ecs_query_t *q = p->poly;
const ecs_filter_t *f = ecs_query_get_filter(q);
test_assert(f != NULL);
test_assert((f->terms[0].src.flags & EcsTraverseFlags) == EcsSelf);
ecs_fini(world);
}
void Pipeline_switch_from_threads_to_tasks(void) {
ecs_world_t* world = ecs_init();
ECS_COMPONENT_DEFINE(world, Position);
ecs_system(world, {
.entity = ecs_entity(world, {.add = { ecs_dependson(EcsOnUpdate) }}),
.query.filter = {.terms = {{ ecs_id(Position) }}},
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, {.add = { ecs_dependson(EcsOnUpdate) }}),
.callback = NoReadonlyAddPosition,
.no_readonly = true
});
ecs_set_threads(world, 2);
ecs_progress(world, 0);
test_int(sys_a_invoked, 0);
test_int(no_staging_add_position_invoked, 1);
test_int(ecs_count(world, Position), 1);
ecs_set_task_threads(world, 2);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(no_staging_add_position_invoked, 2);
test_int(ecs_count(world, Position), 2);
ecs_fini(world);
}
void Pipeline_switch_from_tasks_to_threads(void) {
ecs_world_t* world = ecs_init();
ECS_COMPONENT_DEFINE(world, Position);
ecs_system(world, {
.entity = ecs_entity(world, {.add = { ecs_dependson(EcsOnUpdate) }}),
.query.filter = {.terms = {{ ecs_id(Position) }}},
.callback = SysA
});
ecs_system(world, {
.entity = ecs_entity(world, {.add = { ecs_dependson(EcsOnUpdate) }}),
.callback = NoReadonlyAddPosition,
.no_readonly = true
});
ecs_set_threads(world, 2);
ecs_progress(world, 0);
test_int(sys_a_invoked, 0);
test_int(no_staging_add_position_invoked, 1);
test_int(ecs_count(world, Position), 1);
ecs_set_task_threads(world, 2);
ecs_progress(world, 0);
test_int(sys_a_invoked, 1);
test_int(no_staging_add_position_invoked, 2);
test_int(ecs_count(world, Position), 2);
ecs_fini(world);
}
void Pipeline_run_pipeline_multithreaded_internal(bool task_threads) {
ecs_world_t *world = ecs_init();
ECS_TAG(world, Tag);
ECS_COMPONENT(world, Position);
ecs_entity_t s1 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysA", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysA,
.multi_threaded = true
});
ecs_entity_t s2 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysB", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysB
});
ecs_entity_t s3 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysC", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysC
});
ecs_entity_t s4 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysD", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysD,
.multi_threaded = true
});
ecs_entity_t s5 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysE", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysE,
.multi_threaded = true
});
ecs_entity_t s6 = ecs_system_init(world, &(ecs_system_desc_t){
.entity = ecs_entity(world, { .name = "SysF", .add = {Tag, ecs_pair(EcsDependsOn, EcsOnUpdate)} }),
.query.filter.expr = "Position",
.callback = SysF
});
ECS_PIPELINE(world, P, flecs.system.System, flecs.pipeline.Phase(cascade(DependsOn)), Tag);
test_assert(s1 != 0);
test_assert(s2 != 0);
test_assert(s3 != 0);
test_assert(s4 != 0);
test_assert(s5 != 0);
test_assert(s6 != 0);
ecs_new(world, Position);
ecs_new(world, Position);
if (task_threads)
{
ecs_set_task_threads(world, 2);
}
else
{
ecs_set_threads(world, 2);
}
ecs_run_pipeline(world, P, 1);
test_int(sys_a_invoked, 2);
test_int(sys_b_invoked, 1);
test_int(sys_c_invoked, 1);
test_int(sys_d_invoked, 2);
test_int(sys_e_invoked, 2);
test_int(sys_f_invoked, 1);
ecs_pipeline_stats_t stats = {0};
test_bool(ecs_pipeline_stats_get(world, P, &stats), true);
test_int(ecs_vec_count(&stats.systems), 10);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[0], s1);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[1], 0); /* merge */
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[2], s2);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[3], s3);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[4], 0); /* merge */
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[5], s4);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[6], s5);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[7], 0); /* merge */
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[8], s6);
test_int(ecs_vec_get_t(&stats.systems, ecs_entity_t, 0)[9], 0); /* merge */
ecs_pipeline_stats_fini(&stats);
ecs_fini(world);
}
void Pipeline_run_pipeline_multithreaded(void) {
Pipeline_run_pipeline_multithreaded_internal(false);
}
void Pipeline_run_pipeline_multithreaded_tasks(void) {
Pipeline_run_pipeline_multithreaded_internal(true);
}
void Pipeline_pipeline_init_no_terms(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
test_expect_abort();
ecs_pipeline(world, { 0 });
}
void Pipeline_pipeline_init_no_system_term(void) {
install_test_abort();
ecs_world_t *world = ecs_init();
ECS_COMPONENT(world, Position);
test_expect_abort();
ecs_pipeline(world, {
.query.filter.terms = {{ ecs_id(Position) }}
});
}