Skip to content

Schedule build pass maintenance#19450

Open
PixelDust22 wants to merge 9 commits intobevyengine:mainfrom
PixelDust22:schedule_build_pass_maintenance
Open

Schedule build pass maintenance#19450
PixelDust22 wants to merge 9 commits intobevyengine:mainfrom
PixelDust22:schedule_build_pass_maintenance

Conversation

@PixelDust22
Copy link
Contributor

@PixelDust22 PixelDust22 commented May 31, 2025

Objective

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Jun 2, 2025
@alice-i-cecile alice-i-cecile added the S-Needs-Review Needs reviewer attention (from anyone!) to move forward label Jun 2, 2025
@alice-i-cecile
Copy link
Member

I'd like to see more docs / tests that demonstrate why this functionality is useful, but this is simple enough that I won't block on it. Other reviewers might!

@PixelDust22 PixelDust22 force-pushed the schedule_build_pass_maintenance branch from 8107aa0 to ddcda73 Compare August 26, 2025 18:43
@PixelDust22 PixelDust22 force-pushed the schedule_build_pass_maintenance branch from ddcda73 to eb726f5 Compare August 26, 2025 18:46
Copy link
Contributor

@urben1680 urben1680 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I cannot fully grasp what these changes enable one to do but on a technical level it looks good.

@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Aug 29, 2025
Copy link
Contributor

@chescock chescock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The addition of get_build_pass_mut, the change from &dyn SystemSet to InternedSystemSet, and the default implementations of SchedulePass methods all seem straightforward, and I'd happily approve smaller PRs for those now!

I don't think I understand the use cases for the map_set_to_systems method or for passing &mut World and &mut ScheduleGraph to collapse_set well enough to know whether those are reasonable, though, and those cost some extra hokey-pokeys and a self.hierarchy.topsort.clone(). Can you say more about what you're trying to do?


/// Called when mapping system sets to systems. Implementations may return an iterator of additional systems to be
/// associated with the system set.
fn map_set_to_systems(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you say more about the use case for this new function?

It sounds like it would be for making new automatically-populated system sets, but the API of taking one system set and asking for SystemKeys seems hard to work with. Wouldn't any implementation need to scan the list of systems to find the matching ones? Why not have an API where you ask about one system and return the InternedSystemSets to add?

Also, this API gets called while flattening the graph. If the goal is to add new associations, that might be easier to do before flattening. Would it make sense to instead add a function with a similar signature to build that gets called before flattening, and make something like update_graphs public so that the pass can do arbitrary set and system configuration?

Copy link
Contributor Author

@PixelDust22 PixelDust22 Sep 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new function allows the user to associate additional systems to an existing set.

My use case is that I want to add a prologue system and an epilogue system to some marked system sets. I added a method on App that allows the user to mark some system sets. A schedule build pass adds a prologue system that runs before all systems in the set, and a epilogue that runs after all systems in the set.
image

I used to think that you can do this in collapse_set since it's run for each system. Turns out you can't. If you have a nested system set for example, by the time collapse_set runs, the graph has already been flattened. So you have lost the opportunity to connect the Prologue system with "things that happens before" and the epilogue system with "things that happens after".

I chose this API because it maps best to how we currently build the schedule. The schedule builder roughly does the following:

  1. Topological sort
  2. Create a mapping from SystemSet to all systems contained within the SystemSet.
  3. Flatten the graph
  4. Build.

We currently have hooks for 3 and 4 but not 2. It feels perfectly natural to add a new hook for 2 so that this mapping accurately reflects the hierarchical relationships between systems and system sets.

Also, this API gets called while flattening the graph. If the goal is to add new associations, that might be easier to do before flattening.

That's the whole point of this change. We have the collapse_set hook that runs while flattening the graph. For adding new associations we need a new hook, map_set_to_systems, which runs before flattening the graph.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! That's a neat little bit of functionality :) I think it could be more clearly explained / exposed, but I think it's quite neat.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My use case is that I want to add a prologue system and an epilogue system to some marked system sets.

Thanks, that's a really clear example!

This is the code I have for adding additional systems, and hopefully it shows you why it's necessary to pass down references to &mut ScheduleGraph and &mut World.

Ah, I see, you need &mut World to call System::initialize, because this code runs after ScheduleGraph::initialize. That makes sense, although this is also the sort of thing that worries me. It would be really easy for schedule pass authors to add new systems but forget to initialize them!

That might even be unsound, since we wouldn't initialize the access correctly. The built-in System impls will panic if you run them without calling initialize, but I think it's valid for a third-party impl to run and assume it has world access. ... Oh, ugh, build already takes &mut ScheduleGraph, so maybe this is already a little bit unsound.

For adding new associations we need a new hook, map_set_to_systems, which runs before flattening the graph.

Ah, part of the confusion here might just be different terminology. I think of map_sets_to_systems as flattening the hierarchy graph, while get_dependency_flattened flattens the dependency graph.

Anyway, my point was that it might be easier to add systems and sets in the original hierarchy graph rather than in the middle of map_sets_to_systems.


Would it work to have a method on ScheduleBuildPass that gets called first thing during Schedule::initialize? It should be completely safe to offer &mut Schedule there, since the scheduling code hasn't read anything yet, and it would you you add new systems without needing to remember to initialize them.

You could iterate the system sets using graph.system_sets.iter(), and get the nodes in a set using graph.hierarchy().graph().neighbors_directed(node, Outgoing), so I think the existing public API will let you find the nodes that you'll need to add edges to. (I'm not sure whether your plan is to add the prologue and epilogue to the set itself, and then add dependencies to the other nodes in the set, or whether it's to add them outside the set, and add dependencies to both the set and its dependencies, but either should be possible.)

@alice-i-cecile alice-i-cecile added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged C-Code-Quality A section of code that is hard to understand or change and removed S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it labels Sep 1, 2025
@alice-i-cecile
Copy link
Member

This may ultimately be much easier to land if it's split up into small, well-motivated PRs.

@alice-i-cecile alice-i-cecile self-requested a review September 1, 2025 22:41
@PixelDust22 PixelDust22 requested a review from chescock September 2, 2025 18:44
@alice-i-cecile alice-i-cecile added the M-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide label Sep 2, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Sep 2, 2025

It looks like your PR is a breaking change, but you didn't provide a migration guide.

Please review the instructions for writing migration guides, then expand or revise the content in the migration guides directory to reflect your changes.

@PixelDust22
Copy link
Contributor Author

Migration guide added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ECS Entities, components, systems, and events C-Code-Quality A section of code that is hard to understand or change C-Usability A targeted quality-of-life change that makes Bevy easier to use M-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants