Skip to content

ecs: despawn only what existed before the sync point, or at the end of the run #244

Closed

Description

@krisnye

Summary

commands.despawnWhere (#236) also despawns the matching entities that the same sync point spawns. And a command's effect at an earlier sync point (#232) can despawn data a later system still expects to read. Between the two, an app can't say "clear what existed before, keep what this frame made".

Case (mecs, @adobe/ecs 0.2.4)

One-frame events (a key going down, a request) are spawned during a frame and cleared at the start of the next, so each lives exactly one frame:

export const endFrame = System.create({ components: { with: [frameTransient] } }, (row, { commands }) => {
    commands.despawn(row.entity);
});

Changing the body to commands.despawnWhere({ with: [frameTransient] }) made 5 tests fail: the events spawned in the same sync point are removed before the consumers (selectFace, startStrokes, extrudeSelection) ever see them. A clear-then-respawn (previewBrush: despawn the old preview, spawn the new one) has the same problem. Both stay as per-row despawns.

With #232's earlier sync points the per-row version has a related hazard: endFrame's despawns now apply before later tiers, so it had to be after: [input], and a consumer of an event spawned in the same frame has to be ordered after its producer or the event is gone before the consumer runs. Before, "everything a frame made is read next frame" held without ordering; it is now something to write down.

Requested

  1. despawnWhere that touches only what exists before the sync point (an option, e.g. { existing: true }, or the default with the other documented), or documented as it is.
  2. A despawn "at the end of the run": the command applies at the last sync point, after every system has read, so an event lives until the end of its frame regardless of the schedule.

Activity

  1. krisnye commented on Nov 1, 2026

    @krisnye
    CollaboratorAuthor

    Addressed in #250 (Adobe-Firefly/ecs#9). Request 1 is now the meaning, with no option: despawnWhere despawns only what was alive before its sync point, as the commands issued before it leave it, which is what a row system calling despawn(row.entity) over the same query would do. What that sync point spawns lives. This is why @adobe/ecs goes to 0.3.0.

    Request 2, a despawn at the end of the run, is declined. It would add a command lane that waits past later sync points, which breaks "commands apply at the next sync point, in issue order" and makes an entity's lifetime depend on timing nothing declares. The guide's chapter 06 shows one-frame events instead:

    • the producer clears its last events and spawns this frame's in one body, so every reader sees each event once, wherever it runs (previewBrush too);
    • or endFrame runs after a composite of everything else, which places it last.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions