-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Order independence of App construction #1255
Comments
You probably meant #1144. |
I don't know if this is the right place for this, but I've been reading the discussion (and trying to follow), but is it accurate to say that mostly what a Stage does is just provide a point at which the queued commands get executed? If so, would it be feasible to have a stage-less API that allows someone to simply insert a "run_commands" system and depend on it? |
That's not all a stage does, and is not its most important function: a stage encapsulates a group of systhems and guarantees that they will run before or after another group of systems (encapsulated by a different stage). Everything else just follows from that definition - for example, applying commands issued during the stage at the end of the stage becomes an obvious idea. If you were to try and encode this encapsulation via dependencies, you would have to make the "apply commands" system depend on all systems that come before it and be a dependency for all systems that come after it. This is obviously much less ergonomic, and a lot more error-prone: a system that applies commands can modify archetypes, among other things, and so, with the upcoming non-deterministic executor, can easily produce heisebugs should the user fail to create this synchronisation point. This is not the first time I see "let's get rid of stages" and I still don't fully understand where it's coming from. As I've said before, the schedules and stages hierarchy is strictly organisational; the fact that a stage can be managing several systems doesn't mean that all stages have to be big and slow. A larger, performance-minded project's schedule will most likely consist of a single large heavily-parallelized stage and a handful of small stages performing synchronisation. |
Is it correct that if you want to spawn entities, despawn entities, or modify components, you can only do this at the end of a stage (or rather, you queue them up as commands, and it takes effect then)? You might wish to do these things in the "middle" of a stage. You would have to insert a stage, which might come at the cost of how parallel your systems can run. In some cases you can't, though. A plugin you're using might not have split their systems into stages sufficiently to meet your needs, as you might want to insert a system between some of the plugin's systems within a stage. e.g. plugin_system_a -> my_system -> plugin_system_b The systems you want to run may be mutually exclusive. e.g. you have a set of AI systems, a set of physics systems, and a set of UI layout/update systems; it's likely that each of these can and should run in parallel but (sequential) stages prevent that. Your UI systems might run every frame. On the other hand, your physics system needs to run on a fixed timestep (let's say every 10ms), so it may run several times each frame; your physics system might (will) need to spawn objects between steps in the simulation. Your AI systems may be long-running across multiple frames. It could just be because this is the current convention of using stages to define system execution order which I've seen and are more familiar with, and perhaps in future the default set of stages don't get changed much and we would maybe use SystemSets within the Update stage, but what does this future API properly look like? |
We're still working on that; I'm in the middle of another API pass (not pushed it yet), so keep an eye on #1144. Most of your questions and/or wishes about this will also be more at home there. More on topic; I think the solution for exclusive system ordering in #1144 can be reused for inserting stages, too. It's simple: instead of inserting and sorting stages on the go, just store them, and resolve labels and all the "befores" and "afters" on build, or first run. (Re-reading it now, that's pretty much what you suggest too?) Resources are handled by initialization, I think, which is done on a per-system basis; the burden of that will most likely be shifted completely onto Regarding states... I'm not certain what the plan is, but system sets allow us to reimagine most of |
Another thing that's probably relevant here is how to deal with repeated addition of Plugins. To give an example: If there was a way to only add a plugin if it's not already added that would solve my problem I think. |
When / if we implement this, we should also be mindful of how it interacts with Currently, you should be able to register a If we register all resources before all systems, this won't be possible. I don't think it's a serious issue, but I would like to see an example of how to do this properly made as part of this change. |
Another issue along these lines: the order in which |
A related issue is the following: // this doesn't work
App::build()
.add_plugins(DefaultPlugins)
.add_resource(WindowDescriptor {
title: "My game".into(),
mode: WindowMode::BorderlessFullscreen,
..Default::default()
});
// this works
App::build()
.add_resource(WindowDescriptor {
title: "My game".into(),
mode: WindowMode::BorderlessFullscreen,
..Default::default()
})
.add_plugins(DefaultPlugins); In this case, this is because There are a few separate issues with their own questions, but the gist is that this particular case can be "solved" without necessarily addressing the AppBuilder order independence issue, or perhaps that the AppBuilder order independence issue is a collection of several separate issues that each need addressing:
|
|
Another good example of the problem: inserting
|
Oof that is exceedingly suboptimal. Are there any ideas yet for how we might solve this @alice-i-cecile? |
Yes; this is half the reason behind why I want to move to a structured approach to plugin definition. That should get us most of the way there. |
@alice-i-cecile that's great! Can you please link to the 'structured approach to plugin definition' issue or discussion? |
Most relevant links: #2160, bevyengine/rfcs#33 |
Perfect, thank you! |
From a Discord #help thread. This problem also applies to schedule initialization, and thus states. |
What about building a 'PluginGraph' (similar to schedule graph) with before/after constraints and then calling |
What problem does this solve or what need does it fill?
As with our previous discussion on implicit ordering of systems (see #1144), insertion order mattering is frustrating to debug at scale and can make refactoring more painful.
In addition, this avoids a common beginner pain point, where they'll insert a resource after it's used and then get an error about the resource not existing.
What solution would you like?
Collect all of the App methods, then process them more holistically (such that order doesn't matter) when
AppBuilder.run()
is called.With system insertion order no longer mattering once #1144 is stabilized, there are only a few places that rely on ordering (as best as I can tell).
FromResources
resources are the only wrinkle that comes to mind. We can also catch duplicate insertion of the same type, which is a nice correctness-checking feature.set_runner
. We should probably just panic if two of these are set at once, rather than overwriting.What alternative(s) have you considered?
Through clean code architecture, you can often define resources in the corresponding plugin, before any other systems are added. This reduces the pain substantially, but relies on consistent code hygiene, and won't work well for resources that are used by multiple plugins.
Additional context
Down the road, this approach opens us up to more sophisticated optimizations and analyses, as we'll have access to all of the parts of the
App
before they're processed, rather than only getting a backwards-looking view at best.The text was updated successfully, but these errors were encountered: