Skip to content

Observer ordering/scheduling #14890

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

Open
Azorlogh opened this issue Aug 23, 2024 · 8 comments
Open

Observer ordering/scheduling #14890

Azorlogh opened this issue Aug 23, 2024 · 8 comments
Labels
A-ECS Entities, components, systems, and events A-Picking Pointing at and selecting objects of all sorts C-Feature A new feature, making something new possible D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Design This issue requires design work to think about how it would best be accomplished X-Contentious There are nontrivial implications that should be thought through

Comments

@Azorlogh
Copy link
Contributor

What problem does this solve or what need does it fill?

Sometimes an observer can rely on things added by other observers. But it is currently not possible to explicitly order the observers.

What solution would you like?

I would like the ability to mark an observer as having to run after or before another one.

@Azorlogh Azorlogh added C-Feature A new feature, making something new possible S-Needs-Triage This issue needs to be labelled labels Aug 23, 2024
@Azorlogh
Copy link
Contributor Author

I have two ideas on what the API side can look like:

  • Having something like ObserverEntityCommands::after(Entity), so that you can run it after a specific observer entity
    • This has the downside that you would need to store that entity somewhere if you want to use it in another plugin.
  • Having an ObserverSet type, that you can add to observers, similar to SystemSet.
    • You could then schedule after/before a given ObserverSet without having to worry about the individual observer entities. It's the option I prefer

@Azorlogh Azorlogh changed the title Observer scheduling Observer ordering/scheduling Aug 23, 2024
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events A-Picking Pointing at and selecting objects of all sorts and removed S-Needs-Triage This issue needs to be labelled labels Aug 23, 2024
@alice-i-cecile
Copy link
Member

This is needed in #12365 as well.

@alice-i-cecile alice-i-cecile added X-Contentious There are nontrivial implications that should be thought through D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes labels Aug 23, 2024
@alice-i-cecile
Copy link
Member

alice-i-cecile commented Aug 23, 2024

We definitely want this, just figuring out the architecture and API here is tricky. I'd really like to not be blocked on systems-as-entities and a relationship-powered schedule graph, so a decent temporary solution would be nice here.

@alice-i-cecile alice-i-cecile added the S-Needs-Design This issue requires design work to think about how it would best be accomplished label Aug 24, 2024
@bushrat011899
Copy link
Contributor

I experimented with this idea a little today and there's definitely some non-trivial design constraints we need to consider.

Initially I thought the most obvious answer would be to store observers in a Schedule (rather than the current CachedObservers struct) per event-type E. My rationale was "We want system-like ordering, so why not use the existing solution to that problem?". Since observers are just systems anyway, this makes sense. However, there are some major issues with this approach:

  1. Events propagate, so observers must run one-by-one for a particular event. This means the ability for the schedule to run observers in parallel doesn't matter.
  2. Some observers only watch particular entities or particular components. This isn't unworkable, since we could encode this into run-conditions in a Schedule, but it may be less performant than the current solution (EntityHashMap for observer systems).
  3. The Schedule is a large struct (1kB). Having 1 schedule per event type is just too much overhead.

Unfortunately, I don't have any affirmative ideas on how we should pursue this feature, just the above findings on a way we probably shouldn't do it.

It's a shame events need to propagate (it's non negotiable, too important for UI), because the other two concerns might be counteracted by the new-found ability to run observers in parallel, rather than one-by-one in a single-threaded for-loop (as they currently are). Might be worth changing how Trigger works to make stopping propagation something that's requested separately, and thus observable in the type system. I imagine no matter what we do here, there's a performance win that could come from knowing which observers could be run in parallel, and doing so.

@alice-i-cecile
Copy link
Member

I imagine no matter what we do here, there's a performance win that could come from knowing which observers could be run in parallel, and doing so.

Amdahl's Law is gonna bite you there :) Observers almost always do tiny bits of work: it's unlikely that parallelizing them is worth the overhead.

@cBournhonesque
Copy link
Contributor

cBournhonesque commented Jan 19, 2025

The fact that ObserverMap is a hashmap (https://github.com/bevyengine/bevy/blob/main/crates/bevy_ecs/src/observer/mod.rs#L324) is almost never used apart from observer despawn, so one option could be to switch from a hashmap to a Vec<(Entity, ObserveRunner)>.

Some unclear parts:

  • what would the api look like? Simply app.add_observer(a.after(b))?
  • how would we implement ordering constraints between observers in different maps (global vs per-entity vs per-component observers). I would be fine with defining a fixed order between these (maybe global first, then per-entity, then per-entity) as a first pass

@Peepo-Juice
Copy link
Contributor

Peepo-Juice commented Jan 21, 2025

Just want to mention my situation relating to this. I'm trying to use observers as a way to represent passive abilities on cards. Each card can have a number of abilities and the user might want the abilities to run in a specific order for synergy or math reasons (of course it would only matter if they have the same triggers) . So i can imagine a list of these, with arrows to be able to move them up or down in that list which would control the order of the abilities.

When i first looked into the ObservedBy component, i assumed that changing the Vec inside it would be the way to do it. But I didnt even try it cus i was told early on in the discord that it wasnt going to work. So I think maybe thats another thing to consider if ur thinking about the API for this. I personally would not benefit too much from the before/after API, but thats with respect to my specific situation. I can see that API being useful in other situations so an API that allows for both would be cool. I dont think it matters that it happens specifically in ObservedBy , but that was the first place i looked so maybe its not a bad idea to put it there.

@caniko
Copy link

caniko commented Mar 15, 2025

I have a win-condition observer that I would like to run after several other observers. Would like to have these observers in a systemset-like set so I can run the win-condition after the set. Would be nice.

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 A-Picking Pointing at and selecting objects of all sorts C-Feature A new feature, making something new possible D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Needs-Design This issue requires design work to think about how it would best be accomplished X-Contentious There are nontrivial implications that should be thought through
Projects
Status: Observer overhaul
Development

No branches or pull requests

6 participants