-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Add BundleRemover
#18521
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
Add BundleRemover
#18521
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this is likely to have merge conflicts with #17360.
Option<&mut Table>, | ||
&Components, | ||
&[ComponentId], | ||
) -> (bool, T), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old code intentionally used a const generic instead of an ordinary bool
to make sure that the branches optimized away. Do we need to preserve that?
Even if we don't, it looks like there are only two functions passed as pre_remove
, and one has hard-coded true
and the other hard-coded false
. It might be simpler to make a separate drop: bool
parameter to avoid the tuple and make it clear that it doesn't depend on the values passed to pre_remove
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old code intentionally used a const generic instead of an ordinary
bool
to make sure that the branches optimized away. Do we need to preserve that?
It's an impl Fn, and with only two closures types, monomorphization will effectively the same thing. They are both generic, and both bools are constants.
It might be simpler to make a separate
drop: bool
parameter to avoid the tuple and make it clear that it doesn't depend on the values passed topre_remove
.
I mean, we could add that restriction, but I don't see the point. Letting it depend on it seems more flexible for the future.
&mut deferred_world, | ||
old_archetype, | ||
let mut remover = BundleRemover::new::<T>(self.world, self.location.archetype_id, true)?; | ||
// SAFETY: The passed location has the sane archetype as the remover, since they came from the same location. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to pass the full EntityLocation
to BundleRemover::new
instead of just ArchetypeId
? That would let you get rid of the safety requirement here.
And do you also need to require that this is the right EntityLocation
for the given Entity
? Or would that just be a logic bug and not a soundness bug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to pass the full
EntityLocation
toBundleRemover::new
instead of justArchetypeId
? That would let you get rid of the safety requirement here.
We could but then the remover would be entity specific instead of archetype specific IIUC. So I'd rather leave it as is.
And do you also need to require that this is the right
EntityLocation
for the givenEntity
? Or would that just be a logic bug and not a soundness bug?
Only the archetypes need to match for now I think. In principal, we could make remove
take just a table row and store the archetype id and row and table id on the remover. But this was just easier IMO. I'm open to changing that though!
pub(crate) struct BundleRemover<'w> { | ||
world: UnsafeWorldCell<'w>, | ||
bundle_info: ConstNonNull<BundleInfo>, | ||
old_and_new_table: Option<(NonNull<Table>, NonNull<Table>)>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth trying to make this look more like BundleInserter
? That records the current table
and archetype
pointers along with an enum ArchetypeMoveType
that has the new ones.
Although I suppose one major difference is that you don't need to do anything on a remove that doesn't change archetypes, while an insert might be changing the values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been a while since I wrote this, so I don't remember the details. But IIRC, I originally tried to do this with effectively the same structure as BundleInserter
and ended up running into issues. I think it was easier to justify safety where accessing the new table mutably could be the same as the old table's mutable pointer. I could take another stab at it, but I don't feel it would be worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree it's not worth revisiting. I was just surprised that it looked so different! But if you already tried it that way then there must be a good reason for the differences!
@@ -1361,6 +1364,274 @@ impl<'w> BundleInserter<'w> { | |||
} | |||
} | |||
|
|||
// SAFETY: We have exclusive world access so our pointers can't be invalidated externally |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This safety comment is on a struct
rather than an unsafe
block, so I don't think it will get seen. Did you mean to put it somewhere else, or to put it as a doc comment for the struct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was just copied from BundleInserter
. I don't mind it, but I can understand your point. I wouldn't be opposed to moving it for all Bundle*er
maybe in a different pr.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, okay, I didn't realize this was an existing pattern. That makes sense, then!
crates/bevy_ecs/src/bundle.rs
Outdated
require_all: bool, | ||
) -> Option<Self> { | ||
let bundle_info = world.bundles.get_unchecked(bundle_id); | ||
// SAFETY: Ensured by caller and that intersections are never `None`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this safety comment. What does "and that intersections are never None
" refer to?
The safety requirements for remove_bundle_from_archetype
are "archetype_id
must exist and components in bundle_info
must exist". The caller is ensuring that archetype_id
exists/is valid. Does the fact that you have a BundleId
prove that the components all exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, my comment was poorly written. And yeah, you can't make a bundle without having component ids, so we should be good there.
I don't know if it's policy or just a pattern I've noticed, but since there's two approvals (thanks!), I'll mark ready for final. Let me know if there's some step I'm missing. |
Yup, that's the policy! See https://bevyengine.org/learn/contribute/helping-out/reviewing-pull-requests/#how-pull-requests-are-merged:
(Sorry, I probably should have changed the tag when I approved!) |
You're good! I thought I read that somewhere... |
# Objective It has long been a todo item in the ecs to create a `BundleRemover` alongside the inserter, spawner, etc. This is an uncontroversial first step of bevyengine#18514. ## Solution Move existing code from complex helper functions to one generalized `BundleRemover`. ## Testing Existing tests.
Objective
It has long been a todo item in the ecs to create a
BundleRemover
alongside the inserter, spawner, etc.This is an uncontroversial first step of #18514.
Solution
Move existing code from complex helper functions to one generalized
BundleRemover
.Testing
Existing tests.