Skip to content

Commit

Permalink
Allow for infinitely nested observer events
Browse files Browse the repository at this point in the history
  • Loading branch information
SanderMertens committed Dec 9, 2024
1 parent 03406a7 commit 4a1f4f0
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 108 deletions.
86 changes: 33 additions & 53 deletions distr/flecs.c
Original file line number Diff line number Diff line change
Expand Up @@ -852,10 +852,10 @@ struct ecs_stage_t {
/* Zero if not deferred, positive if deferred, negative if suspended */
int32_t defer;

/* Command queue stack, for nested execution */
/* Command queue */
ecs_commands_t *cmd;
ecs_commands_t cmd_stack[ECS_MAX_DEFER_STACK];
int32_t cmd_sp;
ecs_commands_t cmd_stack[2]; /* Two so we can flush one & populate the other */
bool cmd_flushing; /* Ensures only one defer_end call flushes */

/* Thread context */
ecs_world_t *thread_ctx; /* Points to stage when a thread stage */
Expand Down Expand Up @@ -2791,12 +2791,6 @@ void flecs_enqueue(
ecs_stage_t *stage,
ecs_event_desc_t *desc);

void flecs_commands_push(
ecs_stage_t *stage);

void flecs_commands_pop(
ecs_stage_t *stage);

ecs_entity_t flecs_stage_set_system(
ecs_stage_t *stage,
ecs_entity_t system);
Expand Down Expand Up @@ -9141,26 +9135,18 @@ void ecs_enable_id(
ecs_check(flecs_can_toggle(world, id), ECS_INVALID_OPERATION,
"add CanToggle trait to component");

ecs_entity_t bs_id = id | ECS_TOGGLE;
ecs_add_id(world, entity, bs_id);

if (flecs_defer_enable(stage, entity, id, enable)) {
return;
}

ecs_record_t *r = flecs_entities_get(world, entity);
ecs_entity_t bs_id = id | ECS_TOGGLE;

ecs_record_t *r = flecs_entities_get(world, entity);
ecs_table_t *table = r->table;
int32_t index = -1;
if (table) {
index = ecs_table_get_type_index(world, table, bs_id);
}
int32_t index = ecs_table_get_type_index(world, table, bs_id);
ecs_assert(index != -1, ECS_INTERNAL_ERROR, NULL);

if (index == -1) {
ecs_add_id(world, entity, bs_id);
flecs_defer_end(world, stage);
ecs_enable_id(world, entity, id, enable);
return;
}

ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL);
index -= table->_->bs_offset;
ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL);
Expand Down Expand Up @@ -10611,7 +10597,7 @@ bool flecs_defer_end(

ecs_assert(stage->defer > 0, ECS_INTERNAL_ERROR, NULL);

if (!--stage->defer) {
if (!--stage->defer && !stage->cmd_flushing) {
ecs_os_perf_trace_push("flecs.commands.merge");

/* Test whether we're flushing to another queue or whether we're
Expand All @@ -10621,11 +10607,17 @@ bool flecs_defer_end(
merge_to_world = world->stages[0]->defer == 0;
}

ecs_stage_t *dst_stage = flecs_stage_from_world(&world);
ecs_commands_t *commands = stage->cmd;
ecs_vec_t *queue = &commands->queue;
do {
ecs_stage_t *dst_stage = flecs_stage_from_world(&world);
ecs_commands_t *commands = stage->cmd;
ecs_vec_t *queue = &commands->queue;

if (!ecs_vec_count(queue)) {
break;
}

stage->cmd_flushing = true;

if (ecs_vec_count(queue)) {
/* Internal callback for capturing commands */
if (world->on_commands_active) {
world->on_commands_active(stage, queue,
Expand All @@ -10637,7 +10629,12 @@ bool flecs_defer_end(

ecs_table_diff_builder_t diff;
flecs_table_diff_builder_init(world, &diff);
flecs_commands_push(stage);

if (stage->cmd == &stage->cmd_stack[0]) {
stage->cmd = &stage->cmd_stack[1];
} else {
stage->cmd = &stage->cmd_stack[0];
}

for (i = 0; i < count; i ++) {
ecs_cmd_t *cmd = &cmds[i];
Expand Down Expand Up @@ -10795,18 +10792,18 @@ bool flecs_defer_end(
}
}

stage->cmd_flushing = false;

flecs_stack_reset(&commands->stack);
ecs_vec_clear(queue);
flecs_commands_pop(stage);

flecs_table_diff_builder_fini(world, &diff);

/* Internal callback for capturing commands, signal queue is done */
if (world->on_commands_active) {
world->on_commands_active(stage, NULL,
world->on_commands_ctx_active);
}
}
} while (true);

ecs_os_perf_trace_pop("flecs.commands.merge");

Expand Down Expand Up @@ -17685,22 +17682,6 @@ void flecs_commands_fini(
flecs_sparse_fini(&cmd->entries);
}

void flecs_commands_push(
ecs_stage_t *stage)
{
int32_t sp = ++ stage->cmd_sp;
ecs_assert(sp < ECS_MAX_DEFER_STACK, ECS_INTERNAL_ERROR, NULL);
stage->cmd = &stage->cmd_stack[sp];
}

void flecs_commands_pop(
ecs_stage_t *stage)
{
int32_t sp = -- stage->cmd_sp;
ecs_assert(sp >= 0, ECS_INTERNAL_ERROR, NULL);
stage->cmd = &stage->cmd_stack[sp];
}

ecs_entity_t flecs_stage_set_system(
ecs_stage_t *stage,
ecs_entity_t system)
Expand Down Expand Up @@ -17733,7 +17714,7 @@ ecs_stage_t* flecs_stage_new(
ecs_vec_init_t(a, &stage->post_frame_actions, ecs_action_elem_t, 0);

int32_t i;
for (i = 0; i < ECS_MAX_DEFER_STACK; i ++) {
for (i = 0; i < 2; i ++) {
flecs_commands_init(stage, &stage->cmd_stack[i]);
}

Expand All @@ -17759,7 +17740,7 @@ void flecs_stage_free(
ecs_vec_fini(NULL, &stage->operations, 0);

int32_t i;
for (i = 0; i < ECS_MAX_DEFER_STACK; i ++) {
for (i = 0; i < 2; i ++) {
flecs_commands_fini(stage, &stage->cmd_stack[i]);
}

Expand Down Expand Up @@ -58624,16 +58605,15 @@ void flecs_on_template_set_event(
ecs_assert(ecs_is_deferred(it->world), ECS_INTERNAL_ERROR, NULL);

EcsTemplateSetEvent *evt = it->param;
ecs_suspend_readonly_state_t srs;
ecs_world_t *world = it->real_world;
ecs_assert(flecs_poly_is(world, ecs_world_t), ECS_INTERNAL_ERROR, NULL);

flecs_suspend_readonly(world, &srs);
ecs_defer_suspend(world);

flecs_script_template_instantiate(
world, evt->template_entity, evt->entities, evt->data, evt->count);

flecs_resume_readonly(world, &srs);
ecs_defer_resume(world);
}

/* Template on_set handler to update contents for new property values */
Expand Down
5 changes: 2 additions & 3 deletions src/addons/script/template.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,16 +196,15 @@ void flecs_on_template_set_event(
ecs_assert(ecs_is_deferred(it->world), ECS_INTERNAL_ERROR, NULL);

EcsTemplateSetEvent *evt = it->param;
ecs_suspend_readonly_state_t srs;
ecs_world_t *world = it->real_world;
ecs_assert(flecs_poly_is(world, ecs_world_t), ECS_INTERNAL_ERROR, NULL);

flecs_suspend_readonly(world, &srs);
ecs_defer_suspend(world);

flecs_script_template_instantiate(
world, evt->template_entity, evt->entities, evt->data, evt->count);

flecs_resume_readonly(world, &srs);
ecs_defer_resume(world);
}

/* Template on_set handler to update contents for new property values */
Expand Down
49 changes: 26 additions & 23 deletions src/entity.c
Original file line number Diff line number Diff line change
Expand Up @@ -3636,26 +3636,18 @@ void ecs_enable_id(
ecs_check(flecs_can_toggle(world, id), ECS_INVALID_OPERATION,
"add CanToggle trait to component");

ecs_entity_t bs_id = id | ECS_TOGGLE;
ecs_add_id(world, entity, bs_id);

if (flecs_defer_enable(stage, entity, id, enable)) {
return;
}

ecs_record_t *r = flecs_entities_get(world, entity);
ecs_entity_t bs_id = id | ECS_TOGGLE;

ecs_record_t *r = flecs_entities_get(world, entity);
ecs_table_t *table = r->table;
int32_t index = -1;
if (table) {
index = ecs_table_get_type_index(world, table, bs_id);
}
int32_t index = ecs_table_get_type_index(world, table, bs_id);
ecs_assert(index != -1, ECS_INTERNAL_ERROR, NULL);

if (index == -1) {
ecs_add_id(world, entity, bs_id);
flecs_defer_end(world, stage);
ecs_enable_id(world, entity, id, enable);
return;
}

ecs_assert(table->_ != NULL, ECS_INTERNAL_ERROR, NULL);
index -= table->_->bs_offset;
ecs_assert(index >= 0, ECS_INTERNAL_ERROR, NULL);
Expand Down Expand Up @@ -5106,7 +5098,7 @@ bool flecs_defer_end(

ecs_assert(stage->defer > 0, ECS_INTERNAL_ERROR, NULL);

if (!--stage->defer) {
if (!--stage->defer && !stage->cmd_flushing) {
ecs_os_perf_trace_push("flecs.commands.merge");

/* Test whether we're flushing to another queue or whether we're
Expand All @@ -5116,11 +5108,17 @@ bool flecs_defer_end(
merge_to_world = world->stages[0]->defer == 0;
}

ecs_stage_t *dst_stage = flecs_stage_from_world(&world);
ecs_commands_t *commands = stage->cmd;
ecs_vec_t *queue = &commands->queue;
do {
ecs_stage_t *dst_stage = flecs_stage_from_world(&world);
ecs_commands_t *commands = stage->cmd;
ecs_vec_t *queue = &commands->queue;

if (!ecs_vec_count(queue)) {
break;
}

stage->cmd_flushing = true;

if (ecs_vec_count(queue)) {
/* Internal callback for capturing commands */
if (world->on_commands_active) {
world->on_commands_active(stage, queue,
Expand All @@ -5132,7 +5130,12 @@ bool flecs_defer_end(

ecs_table_diff_builder_t diff;
flecs_table_diff_builder_init(world, &diff);
flecs_commands_push(stage);

if (stage->cmd == &stage->cmd_stack[0]) {
stage->cmd = &stage->cmd_stack[1];
} else {
stage->cmd = &stage->cmd_stack[0];
}

for (i = 0; i < count; i ++) {
ecs_cmd_t *cmd = &cmds[i];
Expand Down Expand Up @@ -5290,18 +5293,18 @@ bool flecs_defer_end(
}
}

stage->cmd_flushing = false;

flecs_stack_reset(&commands->stack);
ecs_vec_clear(queue);
flecs_commands_pop(stage);

flecs_table_diff_builder_fini(world, &diff);

/* Internal callback for capturing commands, signal queue is done */
if (world->on_commands_active) {
world->on_commands_active(stage, NULL,
world->on_commands_ctx_active);
}
}
} while (true);

ecs_os_perf_trace_pop("flecs.commands.merge");

Expand Down
6 changes: 3 additions & 3 deletions src/private_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,10 @@ struct ecs_stage_t {
/* Zero if not deferred, positive if deferred, negative if suspended */
int32_t defer;

/* Command queue stack, for nested execution */
/* Command queue */
ecs_commands_t *cmd;
ecs_commands_t cmd_stack[ECS_MAX_DEFER_STACK];
int32_t cmd_sp;
ecs_commands_t cmd_stack[2]; /* Two so we can flush one & populate the other */
bool cmd_flushing; /* Ensures only one defer_end call flushes */

/* Thread context */
ecs_world_t *thread_ctx; /* Points to stage when a thread stage */
Expand Down
20 changes: 2 additions & 18 deletions src/stage.c
Original file line number Diff line number Diff line change
Expand Up @@ -591,22 +591,6 @@ void flecs_commands_fini(
flecs_sparse_fini(&cmd->entries);
}

void flecs_commands_push(
ecs_stage_t *stage)
{
int32_t sp = ++ stage->cmd_sp;
ecs_assert(sp < ECS_MAX_DEFER_STACK, ECS_INTERNAL_ERROR, NULL);
stage->cmd = &stage->cmd_stack[sp];
}

void flecs_commands_pop(
ecs_stage_t *stage)
{
int32_t sp = -- stage->cmd_sp;
ecs_assert(sp >= 0, ECS_INTERNAL_ERROR, NULL);
stage->cmd = &stage->cmd_stack[sp];
}

ecs_entity_t flecs_stage_set_system(
ecs_stage_t *stage,
ecs_entity_t system)
Expand Down Expand Up @@ -639,7 +623,7 @@ ecs_stage_t* flecs_stage_new(
ecs_vec_init_t(a, &stage->post_frame_actions, ecs_action_elem_t, 0);

int32_t i;
for (i = 0; i < ECS_MAX_DEFER_STACK; i ++) {
for (i = 0; i < 2; i ++) {
flecs_commands_init(stage, &stage->cmd_stack[i]);
}

Expand All @@ -665,7 +649,7 @@ void flecs_stage_free(
ecs_vec_fini(NULL, &stage->operations, 0);

int32_t i;
for (i = 0; i < ECS_MAX_DEFER_STACK; i ++) {
for (i = 0; i < 2; i ++) {
flecs_commands_fini(stage, &stage->cmd_stack[i]);
}

Expand Down
6 changes: 0 additions & 6 deletions src/stage.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,6 @@ void flecs_enqueue(
ecs_stage_t *stage,
ecs_event_desc_t *desc);

void flecs_commands_push(
ecs_stage_t *stage);

void flecs_commands_pop(
ecs_stage_t *stage);

ecs_entity_t flecs_stage_set_system(
ecs_stage_t *stage,
ecs_entity_t system);
Expand Down
3 changes: 2 additions & 1 deletion test/core/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -2244,7 +2244,8 @@
"defer_emplace_after_remove",
"batched_w_table_change_in_observer",
"redefine_named_in_threaded_app",
"batched_cmd_w_component_init"
"batched_cmd_w_component_init",
"deep_command_nesting"
]
}, {
"id": "SingleThreadStaging",
Expand Down
Loading

0 comments on commit 4a1f4f0

Please sign in to comment.