Stageless Problem: User-Added Systems Should "Just Work" #7365
Replies: 5 comments 12 replies
-
Proposal: Fundamental sets
Obviously, Evaluation
|
Beta Was this translation helpful? Give feedback.
-
Proposal: Contiguous schedule graph
The basic idea here is that disconnected systems are fully unmoored from the schedule, and as a result can be run at any random point in the frame. Optionally, we can try automatically repairing this error by adding a default set. Evaluation
|
Beta Was this translation helpful? Give feedback.
-
Proposal: Just use the ambiguity detectorThe system ambiguity order detector is a powerful and fairly fundamental tool. Systems that are unmoored (see above) will have a huge number of ambgiuities, alerting users to the problem. Although it pains my heart to say it, this is a bad solution that doesn't even make it to the evaluation stage. |
Beta Was this translation helpful? Give feedback.
-
Proposal: Exclusive system ambgiuities are fatalWhen detecting execution order ambiguities between systems, error out if any exclusive system has any ambiguities. The logic is twofold:
Optionally, we can try automatically repairing this error by adding a default set. Evaluation
|
Beta Was this translation helpful? Give feedback.
-
Proposal: System ordering hints/// Suggests to the scheduler how to break ties
enum OrderingHint {
/// If this system is ambiguous with a `None` or `Late` system, it runs first.
Early,
/// If this system is ambiguous with an `Early` system it runs second. With `Late` systems, it runs first.
Neutral,
/// If this system is ambiguous with a `Early` or `None` system, it runs second.
Late,
}
// When setting up the core sets
app.configure_set(CoreSet::PreUpdate.hint(OrderingHint::Early);
app.configure_set(CoreSet::PostUpdate.hint(OrderingHint::Late); Systems in By embedding our desired behavior directly into the scheduling graph, we can cause ties to break the way we want, without having to add additional structure. Evaluation
|
Beta Was this translation helpful? Give feedback.
-
Most user-written systems have to do with "gameplay logic".
These systems move enitities around, work with users defined constructs like gold, mana or damage, respond to player input and create effects to be rendered.
Engine and plugin code, by contrast, tends to handle the lower-level primitives needed to make all this work: input, audio, rendering, maintenance and so on.
Within a frame, we can generally divide our systems into three groups:
By default, user-added systems should be treated as gameplay code, and ordering them relative to "engine" code should generally not be a large concern.
Current Solution:
CoreStage::Update
Bevy's initial solution to this was simple.
Create a
CoreStage
enum, which lays out the different stages of each frame.PreUpdate
runs first, thenUpdate
, thenPostUpdate
.User systems are added to
Update
by default, and all was good with the world.The Problem with Stages, in a Nutshell
As evidenced by the fact that we wrote an entire RFC about the problems caused by stages and their kin, this approach was not idyllic.
Let's focus on the most relevant problems:
A
must run beforeB
. We tell it to run beforeB
. If they're in the same stage, this works great! If they're not, we get an inscrutable error discussing howB
was not found. If we start in the same stage, and then refactorA
orB
to live in a different stage, we must hold this constraint (and dozens like it) in our faulty human memory, because it cannot be encoded.plugin_system
that must run afterengine_system
, we cannot simply loosely specify this constraint, and then let the end user precisely coordinate things to match their needs. Instead, we can either overspecify, breaking many users and taking a performance hit, or simply tell users to "read the docs" (which works as well as you might expect).Yeeting Stages == Yeeting Common Sense Defaults?
Ultimately, the stageless RFC said "thou store all of your systems in a single container*".
Stages are out, systems sets (previously labels) are in, command flush points (via
apply_system_buffers
) are just scheduled like any other system.See #6587 for the initial implementation.
To ease migration (#7267) we replaced
CoreStage
withCoreSet
.All of the same sync points are there, and systems have been ported over one-to-one whenever possible.
Since
App::add_system
added things toCoreStage::Update
, it should add things toCoreSet::Update
instead now, right?Right?
Nope, that doesn't work.
A short list of the problems with that idea:
CoreSet::Update
is magical and special-cased. We could make this configurable, but that leads to complex internals and spooky action at a distance when you change the default set before adding a third-party plugin.app.add_system(late_system.in_set(CoreSet::PostUpdate))
panics, aslate_system
is implicitly added toCoreSet::Update
, which must run strictly beforeCoreSet::PostUpdate
.app.add_system(late_system.after(TransformPropagateSystem))
panics, asTransformPropagateSystem
is inCoreSet::PostUpdate
.FixedTimestep
orRenderSchedule
need their own default sets?We can try and fix these footguns by detecting these cases and adding configurability, but this sort of intricate pile of patches is hard to learn and a clear sign that something is deeply wrong with our solution.
Currently, #7267 has punted on this whole problem, given up on default system sets and just manually stuck systems in
CoreSet::Update
when needed.Great for getting that monster merged, not a good plan to ship.
We need to solve this, one way or another, before we ship the next version of Bevy to users.
*unless they don't follow the linear looping control flow, in which case you should run a schedule in an exclusive system.
Solutions and Evaluation Criteria
Now comes the fun bit! You, dear reader, get to propose, comment on and generally bikeshed proposals to fix this!
In the discussion below, make a new top-level reply for each proposal.
Here are some criteria to think about and evaluate when considering proposals.
app.add_system(my_system)
should causemy_system
run before / after things they should generally run before / after.App
to make it nicer.Our goals:
Beta Was this translation helpful? Give feedback.
All reactions