System organization overhaul and the road to a stageless schedule #1375
Replies: 8 comments 8 replies
-
The Proposal
The existing functionality of stages would be covered by:
The Benefits
The Costs
|
Beta Was this translation helpful? Give feedback.
-
Open Questions
|
Beta Was this translation helpful? Give feedback.
-
I'm sorry, too many concepts are misrepresented here. If I correct the mistakes line-by-line the whole thing stops making sense, so I'll just dump thoughts as they come. Systems being exclusive or not is determined solely and automatically from their arguments.
The only thing system sets do is allow attaching run criteria to one or more systems within the same stage. Literally nothing else. There is no overlap between stages and system sets - the former owns the latter. Schedule is not set by stages - it owns them. System sets don't encourage you to scatter systems - they own them. Schedule > stage > set > system. A schedule can be coerced to be a stage. A stage can be stuffed into an exclusive system and be a part of a set. This is not overlap: this is putting things inside other things. There is no "scheduler" - there is an order in which a schedule is executed, and there is no circumstance under which it would make sense to change it. There is no point to execute stages in an order different from the one and only order you've defined for them when building the schedule, that's the whole point of the abstraction: static order between big building blocks of the hierarchy. The executor is an implementation detail of specifically I think the discussion name should have "schedule" and not "scheduler". |
Beta Was this translation helpful? Give feedback.
-
After much discussion on discord, I have an incomplete, but workable API proposal (also relevant to #1312): App::build()
// Stages replaced with a "command set" to reduce confusion: A command set is a system set
// which groups systems whose commands get executed together
// Other than setting hard sync points, they're mostly just ordering systems with a similar API
.add_command_set("stage 1")
// This must be a command set. (probably)
// TODO: Clear up the semantics of how to handle multiple command sets specifying `.before("x")`
// TODO: Consider the implications of letting people use regular ordering sets here
.before("startup")
// if a command set isn't explicitly stated, it defaults to the update stage
// TODO: Consider trying to infer a better alternative if one exists.
.add_system(example_system)
// Systems in this set will not trigger the ambiguity detector for being
// ambiguous on the specified components. In release builds without logging,
// this is a no-op.
.add_ambiguous_set("ambiguous set")
.for_component::<Component1>()
.for_resource::<Resource1>()
// All systems who are part of the set must be in the same command set.
// The run condition gets evaluated once at the start of the stage, and then those who need to
// be checked again run at the end, right before commands.
// TODO: Maybe allow a `parent` conditional set
.add_conditional_set("conditional set", condition_system)
// Ordering sets are used to specify various order trees using `.before` and `.after`
// Multiple, non-interfering set graphs can exist, and they can be as simple as a single node.
// When a system is part of a ordering set, it must run during the execution of said set.
.add_ordering_set("set 1")
.add_ordering_set("set 2").after("set 1")
.add_ordering_set("set 3").after("set 2")
.add_system(
// Inferring the type of system from the given key is done internally.
// TODO: define the specifics of being part of multiple conditional sets (or remove it).
system_1.in_set("conditional set")
// .before and .after calls can only be made to ordering sets.
// The other types, including command sets, should be handled directly using `in_set`
// These should be equivalent to creating a ordering set with a single node,
// setting the given constraining and calling `in_set()`
.before("set 2")
.in_set("different set")
) |
Beta Was this translation helpful? Give feedback.
-
Automatic LabellingI think it would be very useful to automatically apply labels to systems based on their type signatures (or similar information). This would allow us to apply labels to a large number of systems in a consistent, foolproof way using logic that's easily understood, and encode important rules into our schedule without requiring new primitives. As a concrete application, rather than having to special case safety features like #1439, we can use labels to do the lifting in a customizable way. In this case, define a label that is automatically applied to all systems with |
Beta Was this translation helpful? Give feedback.
-
Very preliminary outline of a mad science idea to fully eliminate hard sync points, from Discord: The basic outline goes something like this:
I fully expect there to be some serious feasibility questions around it, but it would: *not actually instantly, but there would be no way to accidentally run your system before commands resolve |
Beta Was this translation helpful? Give feedback.
-
Related discussion in #1512 |
Beta Was this translation helpful? Give feedback.
-
Bears mentioning that there is an ECS project board card with a rough roadmap. |
Beta Was this translation helpful? Give feedback.
-
The Context
This post is an attempt to capture conversation between @Ratysz, @TheRawMeatball, @cart and myself around more ergonomic ways to organize systems. Credit for the original seed of this idea belongs to @Ratysz.
The approach discussed here shouldn't be seriously attempted until 0.5 has been released, in order to make sure the fundamentals of #1144 and Cart's ECS storage overhaul are solidified (see further discussion here).
The Problem
As Bevy's needs grow, there's been an increasing demand for ways to elegantly add "metadata" to systems (which describe how and when a system should execute, rather than what it does). As of 0.4, that includes:
State
or not.This was initially handled by a proliferation of
AppBuilder
methods, such asadd_startup_system_to_stage
, but this has obvious limitations due to the exponential number of combinations as we add metadata. See #1060 for a previous discussion of this problem. I believe @cart has a PR in the works to address this for single systems, by using the builder pattern to specify metadata.Some of this metadata was also handled on a per-stage basis, as seen with SystemStage::serial.
Explicit system ordering was introduced in #1144, to resolve issues around implicit magical behavior that violate least-surprise and make refactoring code bases very dangerous in non-obvious ways. This added another critical, but relatively complex piece of metadata to our systems: dependencies (which specify that a system must run before)
This increases the severity of this problem, and, as part of those changes,
SystemSets
were introduced. These allow you to group systems together, then specify metadata that applies to all the systems within that set. This is a solid improvement over having to specify them one-at-a-time, or tying this data only to aStage
(see the issues aroundStateStage
andrun_criteria
).It is my view that the following difficulties still remain:
Schedule
, determining when 'hard sync' points should be to allow forCommands
to be processed, organizing systems to apply metadata, and ensuring that all of the systems in the previous stage have completed before other systems can proceed. This complex mixture of concerns makes it very hard to reason about when they should be used and what the distinction should be.SystemSets
encourage you to scatter your organizational logic within separate modules / plugins of your code, making it hard to get a big-picture view of how everything fits together.DIGRESSION: Somewhat orthogonal to these concerns, allowing diverse
Schedules
, and especially nestingSchedules
adds a good deal of complexity to our design (and use cases) without a clear benefit. I have yet to see a compelling use of this, either internally or in user code. There should be a way to replace the scheduler with your own, but this doesn't seem to be the way and IMO can be cut from consideration in terms of how to reimplement existing functionality.Beta Was this translation helpful? Give feedback.
All reactions