bevyengine / bevyengine/bevy

Support systems with `In` parameters in schedules

Open
#21,658 3 comments 0 reactions 0 assignees View on GitHub
A-Assets A-ECS C-Feature S-Needs-Design-Doc X-Needs-SME
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 16h
Merged PRs (30d)
171

Description

### Motivation

Recently I’ve been working on scene loading with `bevy_trenchbroom`, and something that’s come up is the way a user can customise how scenes are loaded. Often, a game may want to have some domain-specific metadata that can customise (for example) properties of materials, the components that get applied to newly-created entities, etc. The current solution is using "hooks" - basically a set of domain-specific linked lists of function pointers, inserted at specific points in the loading process with specific responsibilities. A design we came up with is to have all user code be handled by systems that run in the scene's inner `World`. For this, we’d like to be able to define phases, to allow users to ensure the order their code runs in comparison to the processing done in `bevy_trenchbroom` by default. However, in an ideal world we would be able to pass the LoadContext in to the systems, to allow customising loading external dependencies etc. Anything accessible to systems needs to either be added as a resource (in which case it needs to be `'static`, which LoadContext isn’t), or passed in via `run_system_with`. Since the schedule graph uses `Systems`, which bounds systems to use `In = ()`, this means we can’t use schedules to handle this.

### Proposed solution

My proposal would simply be to have `SystemWithAccess`, `Systems`, and `Schedule` have type parameters for input and output, which would both default to `()`. These would not change the implementation at all (users are considered responsible for handling order w.r.t to inputs/outputs), the `new` method for relevant types would only be for the `<(), ()>` instantiation, so users would need to use `Default::default()` to instantiate other versions, and `Schedule::run(..)` would only be available for `Schedule<(), ()>`. For simplicity, instead of introducing `Schedule::run_with`, the way to actually run these systems would probably be to just use `topsort_graph` and run the systems in sequence. On the topic of simplicity, I also think it’s fine to require all systems in `Systems`/`Schedule` to have the same inputs and outputs, rather than letting them store dynamic system IDs. This is a very minimal change but would be a nice quality-of-life feature for anyone who uses schedules outside of the context of an `App` and/or wants to run a one-shot schedule.

For the purposes of scene loading, I do feel that loading scenes that include entities with behaviour has unique-enough requirements that something more dedicated built in to Bevy could be useful, but this would, at least, give us primitives that we can experiment with.

### Alternatives

The standard way to handle the original motivation is, as far as I know, to load all the assets in some basic form, wait for them to be loaded, then process them in the main loop to create the relevant entities. This works, but has a few issues:

- The systems operate on the main world instead of in a completely separate world in a completely separate thread (potentially causing frame drops if significant processing is needed).
- It doesn’t have easy access to assets before handles have been created from them, for example if you want to edit an image before applying it to a material you need to mark it as being CPU-accessible
- Loading is a one-shot process, and you should ideally not have these systems in the main loop

Contributor guide

Open the contributing guide

Research direction

Start by reading the SystemWithAccess, Systems, and Schedule types, then trace topsort_graph and Schedule::run. The proposed change is to support shared input and output type parameters while retaining () defaults and existing construction and run behavior for the default instantiation. Done means schedules can represent systems with In parameters and be run sequentially as described.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
game-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.