-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Per world access lists and utils #17198
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
Per world access lists and utils #17198
Conversation
Design Decision: When tracking system exclusivity (ie: &mut World, etc), should we handle exclusive systems as exclusive to only their referenced world or as exclusive to their home world? I chose exclusive to their home world, as it was easier to implement To clarify, if there is a resource in the main world that holds another world, and a system requires exclusive access to the nested world, it currently gets exclusive access to the main world (the system's home world) too. To change this, we would need to return a list from `System::is_exclusive` instead of just a bool. This may be worth looking at in future PRs. In the meantime, to fulfill the simple solution, I refactored conflicting system record into their own enum instead of having a four-element tuple with special meanings. I did my best with the conflict reporting messages. It looks like there is some confusion in the pre-existing comments about how to handle conflicts from conflicting non-exclusive systems that have no conflicting components. The comment now on line 1422 describes one way this can happen.
This was very smooth and user-friendly. This is an indication to me that this may be the kind of implementation we should go for, though this implementation is still very much a prototype so far.
A note on Cargo.toml: cargo fmt changed the indent of the file. I'm assuming, this is because rustfmt.toml changed the spacing it preferred. I had to add the const_new feature to small_vec to maintain the const new api of UniversalAccess.
I didn't see a way around adding `SystemParam::update_meta`. It is not ideal, but it makes sense to have it, and we certainly needed it here. I removed a todo comment looking for an early out for updating archetypes. This is because such an out would be impractical now that linked worlds would need to be checked. I made the `Link` resource private to its file to enable some scary unsafe access. The comments have more details, but the idea is that this ensures it is only accessed from the file, and we can more easily enforce manual synchronization from there.
see updated docs for Link for more. I was previously prototyping a way to allow nested exclusive access, before realizing it was not necessary anyway since `&mut World` can't go in `Linked` anyway.
Welcome, new contributor! Please make sure you've read our contributing guide and we look forward to reviewing your pull request shortly ✨ |
These were caught by bevy ci. Thanks bevy ci.
System query and resource access api looks great. It would be nice if linked world is SubApp instead of World. This would allow to apply different set of plugins for SubApp/Subworld. And subworld schedule could be executed somewhere in main world schedule manually by taking Full mutable access to Subapp/Subworld or maybe even executed in parallel in far future. Overall great work and looking forward to this functionality :) |
And looks like there is still no answer to data extraction question #8143 from subapp and this API would be greatly appreciated. |
I agree with that in the long run. Or even, make some sort of WorldPlugin since most Plugins only use the world anyway and don't set an extraction function or anything. I also want to be able to add these linked worlds at runtime though. I don't think you can create a new subapp for the main app once it starts. Its also worth noting that you can, very carefully, use a raw pointer to a world as a WorldLink. The linked world doesn't have to live in the main world. You could potentially insert a pointer to a subapp's world into the main world as a WorldLink from the subapp's extraction phase. You would also need to create some form of synchronization or maybe a custom schedule to keep the access safe, but it should be possible to use this interface with subapps. |
Agree, It is important that linked SubApp/World can be recreated/removed/replaced during runtime. Some questions about planned interface that I thought of.
|
Yes, but you need to do it through exclusive access to the main world. For example: fn nested_exclusive(main: &mut World) {
let nested = main..peek_link::<NestedWorld>().unwrap()
}
Absolutely. This should be pretty strait forward to add in the future, but for now, I'm trying to keep this already massive PR as slim as possible. If this is merged, you can expect a number of follow up PRs, adding more utilities/interfaces to make this even more useful.
I'm not sure if I am 100% on what you're asking here, but here is something you could do: struct SubAppLink(*mut World);
impl WorldLink for SubAppLink {
// insert unsafe pointer magic here
}
fn sub_app_extraction(sub_world: &mut World, main_world: &mut World) {
main_world.link(SubAppLink(sub_world));
main_world.run_schedule(MyCustomSchedule);
main_world.unlink::<SubAppLink>();
}
fn main_system_for_custom_schedule(from_sub_app: Linked<SubAppLink, AnyParamHere>) {
// whatever work you need on the main world but with reference to the sub app.
// You could even make this function exclusive and run more schedules on the sub app here. Endless possibilities!
} I cut some corners, but that's the idea. If you want more free-flowing communication with a custom synchronization system, the Linked interface won't be enough. The per-world-access lists will still be foundational to any interface that does that though. |
This was erroneously removed while resolving a merge conflict.
This reverts commit d422d48.
I had to revert the previous attempt at fixing this since there were more things wrong than I initially thought. It should all be working fine now,
@BenjaminBrienen Thanks! Merging should be good now. This has gotten positive feedback in both discord and github, but if there is anything you or anyone else wants changed, let me know. |
Alright, weighing in in-thread as SME-ECS. Sorry for leaving you hanging, and thanks a ton for providing the additional context. From a technical perspective, this seems well-executed, and I definitely understand why you might want to move in this direction: multiple worlds are useful, and being able to multithread your scheduling across them is a really cool idea. That said, I'm reluctant to move forward with this right now. As @wrongshoe said on Discord.
As it currently stands, increasing multi-threading will likely worsen Bevy's performance. @NthTensor has a secret project to reduce that overhead that will likely change that calculus, and I have long-standing hopes of trying to precompute parallelism strategies through schedules to batch systems, so I don't think that's an unchangeable truth. Just "probably not worth the overhead and complexity right now". More broadly, there's increasing appetite for real multi-world support in Bevy. The existing Providing more tools to support using multiple worlds without a clear design in this space is a recipe for maintainer headaches (whee tech debt) and user frustration. There's no blessed mechanism or documentation for even the simplest of operations (sending data between worlds), and while you can hack around the limitations, intermediate users will struggle terribly and the ecosytem will fragment as advanced users write their own hacks. Tackling tech debt in this area (both the Decoupled Rendering and the Turtles All the Way Down working groups are doing this) should come first, but after that I would like to spin up a true Multiworld working group, where a design in this vein (cross-world multithreaded scheduling) can be discussed in earnest. |
Wow. Great points. That's why I asked for an expert! You're absolutely right that this should at the very least wait until precomputed schedules. I didn't even know that was possible or being discussed, but now I'm excited. I agree with your opinion on sub-apps too. I have also been thinking about more free-flowing multi-world coordination beyond sub-apps, so I'm glad to hear I'm not the only one. My end goal with this was to get multiple worlds communicating in one app, and it sounds like that is in Bevy's future, right after bsn, editors, and half a dozen way more important stuff. Summarizing the discussion as I understand it:
I will look forward to the multi-world working group and what they put together. In the meantime, I'm going to let this go cold, and come back to my multi-world project later. Feel free to close the PR when a better solution is proposed down the road. One last thing in case it interests anyone: In the per-world access lists, the list stores a small vec of normal (one world) access lists. If we restrict that vec's inline capacity to 1, I'm inclined to think there wouldn't really be any additional scheduling overhead since it still only checks one world. That would allow a temporary fix that might provide a more convinient alternative to the hack arounds you mentioned. That said, given the other points you made, I'm not even convinced myself that it would even be worth it. I'll leave it up to your judgement. |
Objective
Fixes #16933
Note
This PR proposes a direct solution to #16933. The issue is technically still in design phase, and while there may be better solutions, I wanted to get the conversation going. This is the least complex solution I could think of.
Since this PR is sort of a base-line implementation to create some discussion, I haven't done mush polish. If the community thinks this sort of implementation is worth pursuing, I would want to put more time into polishing it up with more documentation, tests, and maybe an example.
Solution
I made four main changes to facilitate this. These could be split into smaller PRs later.
First, I added universal access lists. See crates/bevy_ecs/src/query/access.rs
UniversalAccess
. This stores multipleAccess
s, one per relevant world. This lets access be tracked over multiple worlds. I used aSmallVec
instead of aHashMap
for pairing worlds to accesses because I suspect it will perform better, but this could always be changed.Second, I converted
SystemMeta
andFilteredAccessSet
to useUniversalAccess
. I also updated the relevant references. This formed the backbone for the feature since it allows systems to be parallelized across multiple world accesses.Third, I created an abstraction in crates/bevy_ecs/src/world/links.rs that makes it easy to nest or reference worlds in each other. This is what makes the feature useful.
Fourth, I added
SystemParam::update_meta
. I needed a way to notify nested parameters of archetype changes. This needed to be within the scope ofSystem::update_archetype_component_access
, otherwiseSystemMeta
may be changed in a way that conflicts with the scheduler or the nested param my not have been fully updated. Using this new interface, I was able to fully connect nested params.As a note, the
Link
abstraction uses some sneaky privacy rules to safely access a resource in a way that is traditionally unsafe. Although I don't see any issues with it, I encourage it to be very carefully reviewed in particular.Testing
I have done minimal testing myself. If the community likes this sort of implementation, I would want more tests for sure. If anyone has any requests for more tests, I'd be happy to add them.
The current CI passes for me. I'm on a M2 Mac.
As far as I can tell, though not polished, the current state of the PR is safe, so please try to break it.
Showcase
The following is an excerpt of the test I wrote for a proof on concept. See crates/bevy_ecs/src/world/links.rs for full context and refer to #16933 for the long-term vision.
This kind of interface could make sub app extraction much much easier to work with (and likely faster from parallelization). In the long run, it may also enable multiple sub apps' extractions to be run in parallel. This would be huge for performance of projects with multiple big sub apps, like those using avian physics.
Migration Guide
Most notably,
SystemMeta
's access data types have changed. If the community is in favor of this kind of implementation, I can work on a more complete migration guide.Final Note:
This is my first time contributing to bevy or open source in general, so I am extremely open to feedback. I have done my best to follow the contributing guidelines, but let me know if anything needs to be done differently. I'm very excited about this feature, so any feedback or requests are very welcome.