bevy/crates/bevy_ecs/src/schedule
Chris Russell 0ee937784e
Simplify self-edge checking in schedule building (#20015)
# Objective

Make the schedule graph code more understandable, and replace some
panics with `Result`s.

I found the `check_edges` and `check_hierarchy` functions [a little
confusing](https://github.com/bevyengine/bevy/pull/19352#discussion_r2181486099),
as they combined two concerns: Initializing graph nodes for system sets,
and checking for self-edges on system sets. It was hard to understand
the self-edge checks because it wasn't clear what `NodeId` was being
checked against! So, let's separate those concerns, and move them to
more appropriate locations.

Fix a bug where `schedule.configure_sets((SameSet, SameSet).chain());`
would panic with an unhelpful message: `assertion failed: index_a <
index_b`.

## Solution

Remove the `check_edges` and `check_hierarchy` functions, separating the
initialization behavior and the checking behavior and moving them where
they are easier to understand.

For initializing graph nodes, do this on-demand using the `entry` API by
replacing later `self.system_set_ids[&set]` calls with a
`self.system_sets.get_or_add_set(set)` method. This should avoid the
need for an extra pass over the graph and an extra lookup.

Unfortunately, implementing that method directly on `ScheduleGraph`
leads to borrowing errors as it borrows the entire `struct`. So, split
out the collections managing system sets into a separate `struct`.

For checking self-edges, move this check later so that it can be
reported by returning a `Result` from `Schedule::initialize` instead of
having to panic in `configure_set_inner`. The issue was that `iter_sccs`
does not report self-edges as cycles, since the resulting components
only have one node, but that later code assumes all edges point forward.
So, check for self-edges directly, immediately before calling
`iter_sccs`.

This also ensures we catch *every* way that self-edges can be added. The
previous code missed an edge case where `chain()`ing a set to itself
would create a self-edge and would trigger a `debug_assert`.
2025-07-08 18:09:45 +00:00
..
executor Have System::run_unsafe return Result. (#19145) 2025-07-03 21:48:09 +00:00
graph Use SlotMaps to store systems and system sets in Schedules (#19352) 2025-07-03 18:50:54 +00:00
auto_insert_apply_deferred.rs Use SlotMaps to store systems and system sets in Schedules (#19352) 2025-07-03 18:50:54 +00:00
condition.rs Rename send_event and similar methods to write_event (#20017) 2025-07-07 22:05:16 +00:00
config.rs Have System::run_unsafe return Result. (#19145) 2025-07-03 21:48:09 +00:00
mod.rs Simplify self-edge checking in schedule building (#20015) 2025-07-08 18:09:45 +00:00
pass.rs Use SlotMaps to store systems and system sets in Schedules (#19352) 2025-07-03 18:50:54 +00:00
schedule.rs Simplify self-edge checking in schedule building (#20015) 2025-07-08 18:09:45 +00:00
set.rs Have System::run_unsafe return Result. (#19145) 2025-07-03 21:48:09 +00:00
stepping.rs Use SlotMaps to store systems and system sets in Schedules (#19352) 2025-07-03 18:50:54 +00:00