bevy_ecs: add untyped methods for inserting components and bundles#5602
bevy_ecs: add untyped methods for inserting components and bundles#5602jakobhellermann wants to merge 4 commits intobevyengine:mainfrom
Conversation
|
Pining here to see if there is any plans to pursue this PR? |
|
ping @jakobhellermann |
Sorry for not responding earlier. I'm a bit busy with Uni right now but feel free to adopt this PR. |
2e1fe5a to
199c646
Compare
|
I rebased the PR and adressed the review comments. It's not quite ready for review yet because I made the |
199c646 to
34c9332
Compare
| pub struct Bundles { | ||
| bundle_infos: Vec<BundleInfo>, | ||
| bundle_ids: HashMap<TypeId, BundleId>, | ||
| bundle_ids_dynamic: HashMap<Vec<ComponentId>, BundleId>, |
There was a problem hiding this comment.
The Vec<ComponentId> could be reduced to a Box<[ComponentId]> since it's never appending elements, therefore the entire Vec is not needed.
There was a problem hiding this comment.
Creating boxed slices will induce a lot of unnecessary reallocations which are far more expensive than the 8 bytes you save of not having a Vec.
There was a problem hiding this comment.
ahh yes true, forgot the Vec would need to drop any excess storage and possibly reallocate. Not just a simple pointer transfer.
34c9332 to
25115e8
Compare
|
rebased the PR |
alice-i-cecile
left a comment
There was a problem hiding this comment.
I've taken a look, and this seems solid from an API side. @DJMcNab, you made a very similar PR for me at one point, can I get your review?
There was a problem hiding this comment.
I'm not quite sure on the API design here; allocating vectors every time you need this is unfortunate.
I also think that the ordering things require more careful consideration.
DynamicBundle also needs to be unsafe I believe. Hmm, technically if all the APIs which use it require it to be implemented correctly as part of their contracts, maybe not?
OTOH, I don't mind making this slow that much; people should just write their game code in Rust.
| mut component_ids: Vec<ComponentId>, | ||
| components: I, | ||
| ) -> &mut Self { | ||
| component_ids.sort(); |
There was a problem hiding this comment.
This is not obvious correct.
Do you not also need to simultaneously sort the components?
|
This PR looks amazing and is super helpful! I was looking into implementing something similar and would like to throw a suggestion up for discussion, Would it be possible to move the The resulting This would have the added benefit of being able to remove Could rename EDIT: Dumped the changes in jakobhellermann#69 |
|
Controversial because we need to decide between this and #7204 :) |
…7204) This MR is a rebased and alternative proposal to #5602 # Objective - #4447 implemented untyped (using component ids instead of generics and TypeId) APIs for inserting/accessing resources and accessing components, but left inserting components for another PR (this one) ## Solution - add `EntityMut::insert_by_id` - split `Bundle` into `DynamicBundle` with `get_components` and `Bundle: DynamicBundle`. This allows the `BundleInserter` machinery to be reused for bundles that can only be written, not read, and have no statically available `ComponentIds` - Compared to the original MR this approach exposes unsafe endpoints and requires the user to manage instantiated `BundleIds`. This is quite easy for the end user to do and does not incur the performance penalty of checking whether component input is correctly provided for the `BundleId`. - This MR does ensure that constructing `BundleId` itself is safe --- ## Changelog - add methods for inserting bundles and components to: `world.entity_mut(entity).insert_by_id`
Objective
TypeId) APIs for inserting/accessing resources and accessing components, but left inserting components for another PR (this one)Solution
EntityMut::{insert_by_id, insert_bundle_by_id}BundleintoDynamicBundlewithget_componentsandBundle: DynamicBundle. This allows theBundleInsertermachinery to be reused for bundles that can only be written, not read, and have no statically availableComponentIdsBundles::bundle_ids_dynamic: HashMap<Vec<ComponentId>, BundleId>next to theHashMap<TypeId, BundleId>Changelog
world.entity_mut(entity).insert_bundle_by_id/insert_by_idUnresolved questions
can we get rid of some allocations?
init_info_dynamiccould remove a.clone()of aVec<ComponentId>whenHashMap::raw_entrystabilizesinsert_by_idcould not reuseinsert_bundle_by_idto avoid theVecoverhead, or alternativelyinsert_bundle_by_idcould maybe be changed to not require theVecfor theOwningPtrscurrently
insert_bundle_by_idsrequires takes aVec<ComponentId>that must be sorted and aVec<OwningPtr<'_>that must match the component ids. This is because internally we also need theVec<ComponentId>so if the caller already has it we can just use that.&[(ComponentId, OwningPtr<'_>)]and internally collect that into a sorted vec of component ids?ComponentIdvec in the function so that we don't have such a subtle safety requirement? sorting an already sorted vec is the best case scenario for insertion sort (which is used forstdsort for <=20 elements) so it's just a smallO(n)cost