Skip to content

Conversation

ElliottjPierce
Copy link
Contributor

@ElliottjPierce ElliottjPierce commented Mar 20, 2025

Objective

#18173 allows components to be queued without being fully registered. But much of bevy's debug logging contained components.get_name(id).unwrap(). However, this panics when the id is queued. This PR fixes this, allowing names to be retrieved for debugging purposes, etc, even while they're still queued.

Solution

We change ComponentInfo::descriptor to be Arc<ComponentDescriptor> instead of not arc'd. This lets us pass the descriptor around (as a name or otherwise) as needed. The alternative would require some form of MappedRwLockReadGuard, which is unstable, and would be terribly blocking. Putting it in an arc also signifies that it doesn't change, which is a nice signal to users. This does mean there's an extra pointer dereference, but I don't think that's an issue here, as almost all paths that use this are for debugging purposes or one-time set ups.

Testing

Existing tests.

Migration Guide

Components::get_name now returns Option<Cow<'_, str> instead of Option<&str>. This is because it now returns results for queued components. If that behavior is not desired, or you know the component is not queued, you can use components.get_info().map(ComponentInfo::name) instead.

Similarly, ScheduleGraph::conflicts_to_string now returns impl Iterator<Item = (String, String, Vec<Cow<str>>)> instead of impl Iterator<Item = (String, String, Vec<&str>)>. Because Cow<str> derefs to &str, most use cases can remain unchanged.

@alice-i-cecile alice-i-cecile added this to the 0.16 milestone Mar 20, 2025
@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use S-Needs-Review Needs reviewer attention (from anyone!) to move forward M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide labels Mar 20, 2025
queued
.components
.values()
.find(|queued| queued.id == id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The linear search here looks expensive. Instead of putting the descriptors in the individual queues, would it make sense to have a separate HashMap<ComponentId, Arc<ComponentDescriptor>>? For that matter, the ComponentIds are dense, so I think it could be Vec<Arc<ComponentDescriptor>>.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the linear search is not ideal, but this is a cold path. Ids are almost always correct unless this is in a inter-world context, and queued components aren't queued for long. I genuinely think it would be slower to cache it's location somewhere or change the backing data structure because that slows everything down by a tiny bit, where as this slows almost nothing down (but by a lot).

That's why I did it this way (even though it hurts my eyes too). But if the consensus is different, I can absolutely do otherwise.

@alice-i-cecile alice-i-cecile requested a review from chescock March 31, 2025 17:52
Copy link
Contributor

@chescock chescock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some suggestions, but neither of them should be blocking.

The linear scan still makes me nervous! I'd feel better if we instead added a Vec<ComponentDescriptor> to QueuedRegistration. We know that it's a dense range of indices, so we can index it by offsetting ComponentId.

But if we're okay with the linear scan, then this is a solid implementation of that approach and we should merge it!

.components
.values()
.find(|queued| queued.id == id)
.map(|queued| CowArc::Owned(Arc::new(queued.descriptor.clone())))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to make queued.descriptor be an Arc, so that this just clones the Arc? Right now, you're cloning the underlying descriptor and allocating a new Arc each time.

If you do want to clone the descriptor, then there's no need for an Arc and you can return an ordinary Cow<ComponentDescriptor> here.

For that matter, if you're willing to just have get_name and not a general get_descriptor, you could return Cow<str> and just have to clone name. It's a Cow::Borrowed in all cases except dynamic components, so that will usually be a cheap reference copy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this was a mess.

Originally it was an arc, which is why we needed a new release of atomicow.
But it being an arc, slowed down other operations because of the extra deref, so we removed it.
So this is basically an artifact. Nice catch.

Now it's removed and we can (ironically) just do normal Cows.

@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Mar 31, 2025
ElliottjPierce and others added 2 commits March 31, 2025 15:40
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
@ElliottjPierce
Copy link
Contributor Author

The linear scan still makes me nervous! I'd feel better if we instead added a Vec<ComponentDescriptor> to QueuedRegistration. We know that it's a dense range of indices, so we can index it by offsetting ComponentId.

It hurts me to see something unoptimized too, but this is a really slow path, and I think introducing an invariant (even though it's incidentally upheld today), is not worth it.


The migration guide has also been updated BTW.

auto-merge was automatically disabled March 31, 2025 20:04

Head branch was pushed to by a user without write access

ElliottjPierce and others added 2 commits March 31, 2025 16:34
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Mar 31, 2025
Merged via the queue into bevyengine:main with commit 5db67f3 Mar 31, 2025
36 checks passed
mockersf pushed a commit that referenced this pull request Apr 3, 2025
# Objective

#18173 allows components to be queued without being fully registered.
But much of bevy's debug logging contained
`components.get_name(id).unwrap()`. However, this panics when the id is
queued. This PR fixes this, allowing names to be retrieved for debugging
purposes, etc, even while they're still queued.

## Solution

We change `ComponentInfo::descriptor` to be `Arc<ComponentDescriptor>`
instead of not arc'd. This lets us pass the descriptor around (as a name
or otherwise) as needed. The alternative would require some form of
`MappedRwLockReadGuard`, which is unstable, and would be terribly
blocking. Putting it in an arc also signifies that it doesn't change,
which is a nice signal to users. This does mean there's an extra pointer
dereference, but I don't think that's an issue here, as almost all paths
that use this are for debugging purposes or one-time set ups.

## Testing

Existing tests.

## Migration Guide

`Components::get_name` now returns `Option<Cow<'_, str>` instead of
`Option<&str>`. This is because it now returns results for queued
components. If that behavior is not desired, or you know the component
is not queued, you can use
`components.get_info().map(ComponentInfo::name)` instead.

Similarly, `ScheduleGraph::conflicts_to_string` now returns `impl
Iterator<Item = (String, String, Vec<Cow<str>>)>` instead of `impl
Iterator<Item = (String, String, Vec<&str>)>`. Because `Cow<str>` derefs
to `&str`, most use cases can remain unchanged.

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
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 C-Usability A targeted quality-of-life change that makes Bevy easier to use M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants