b14f94ec34
171 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
![]() |
4562bb484f |
Fix spawn tracking for spawn commands (#19351)
See also https://discord.com/channels/691052431525675048/1374187654425481266/1375553989185372292. Set spawn info in `Commands::spawn_empty`. Also added a benchmark for `Commands::spawn`. See added test. |
||
![]() |
9666a7e688 |
Rename bevy_platform_support to bevy_platform (#18813)
The goal of `bevy_platform_support` is to provide a set of platform agnostic APIs, alongside platform-specific functionality. This is a high traffic crate (providing things like HashMap and Instant). Especially in light of https://github.com/bevyengine/bevy/discussions/18799, it deserves a friendlier / shorter name. Given that it hasn't had a full release yet, getting this change in before Bevy 0.16 makes sense. - Rename `bevy_platform_support` to `bevy_platform`. |
||
![]() |
1553ee98ff |
Switch ChildOf back to tuple struct (#18672)
# Objective In #17905 we swapped to a named field on `ChildOf` to help resolve variable naming ambiguity of child vs parent (ex: `child_of.parent` clearly reads as "I am accessing the parent of the child_of relationship", whereas `child_of.0` is less clear). Unfortunately this has the side effect of making initialization less ideal. `ChildOf { parent }` reads just as well as `ChildOf(parent)`, but `ChildOf { parent: root }` doesn't read nearly as well as `ChildOf(root)`. ## Solution Move back to `ChildOf(pub Entity)` but add a `child_of.parent()` function and use it for all accesses. The downside here is that users are no longer "forced" to access the parent field with `parent` nomenclature, but I think this strikes the right balance. Take a look at the diff. I think the results provide strong evidence for this change. Initialization has the benefit of reading much better _and_ of taking up significantly less space, as many lines go from 3 to 1, and we're cutting out a bunch of syntax in some cases. Sadly I do think this should land in 0.16 as the cost of doing this _after_ the relationships migration is high. |
||
![]() |
8723096d57 |
reexport entity set collections in entity module (#18413)
# Objective Unlike for their helper typers, the import paths for `unique_array::UniqueEntityArray`, `unique_slice::UniqueEntitySlice`, `unique_vec::UniqueEntityVec`, `hash_set::EntityHashSet`, `hash_map::EntityHashMap`, `index_set::EntityIndexSet`, `index_map::EntityIndexMap` are quite redundant. When looking at the structure of `hashbrown`, we can also see that while both `HashSet` and `HashMap` have their own modules, the main types themselves are re-exported to the crate level. ## Solution Re-export the types in their shared `entity` parent module, and simplify the imports where they're used. |
||
![]() |
c2854a2a05
|
bevy_reflect: Deprecate PartialReflect::clone_value (#18284)
# Objective #13432 added proper reflection-based cloning. This is a better method than cloning via `clone_value` for reasons detailed in the description of that PR. However, it may not be immediately apparent to users why one should be used over the other, and what the gotchas of `clone_value` are. ## Solution This PR marks `PartialReflect::clone_value` as deprecated, with the deprecation notice pointing users to `PartialReflect::reflect_clone`. However, it also suggests using a new method introduced in this PR: `PartialReflect::to_dynamic`. `PartialReflect::to_dynamic` is essentially a renaming of `PartialReflect::clone_value`. By naming it `to_dynamic`, we make it very obvious that what's returned is a dynamic type. The one caveat to this is that opaque types still use `reflect_clone` as they have no corresponding dynamic type. Along with changing the name, the method is now optional, and comes with a default implementation that calls out to the respective reflection subtrait method. This was done because there was really no reason to require manual implementors provide a method that almost always calls out to a known set of methods. Lastly, to make this default implementation work, this PR also did a similar thing with the `clone_dynamic ` methods on the reflection subtraits. For example, `Struct::clone_dynamic` has been marked deprecated and is superseded by `Struct::to_dynamic_struct`. This was necessary to avoid the "multiple names in scope" issue. ### Open Questions This PR maintains the original signature of `clone_value` on `to_dynamic`. That is, it takes `&self` and returns `Box<dyn PartialReflect>`. However, in order for this to work, it introduces a panic if the value is opaque and doesn't override the default `reflect_clone` implementation. One thing we could do to avoid the panic would be to make the conversion fallible, either returning `Option<Box<dyn PartialReflect>>` or `Result<Box<dyn PartialReflect>, ReflectCloneError>`. This makes using the method a little more involved (i.e. users have to either unwrap or handle the rare possibility of an error), but it would set us up for a world where opaque types don't strictly need to be `Clone`. Right now this bound is sort of implied by the fact that `clone_value` is a required trait method, and the default behavior of the macro is to use `Clone` for opaque types. Alternatively, we could keep the signature but make the method required. This maintains that implied bound where manual implementors must provide some way of cloning the value (or YOLO it and just panic), but also makes the API simpler to use. Finally, we could just leave it with the panic. It's unlikely this would occur in practice since our macro still requires `Clone` for opaque types, and thus this would only ever be an issue if someone were to manually implement `PartialReflect` without a valid `to_dynamic` or `reflect_clone` method. ## Testing You can test locally using the following command: ``` cargo test --package bevy_reflect --all-features ``` --- ## Migration Guide `PartialReflect::clone_value` is being deprecated. Instead, use `PartialReflect::to_dynamic` if wanting to create a new dynamic instance of the reflected value. Alternatively, use `PartialReflect::reflect_clone` to attempt to create a true clone of the underlying value. Similarly, the following methods have been deprecated and should be replaced with these alternatives: - `Array::clone_dynamic` → `Array::to_dynamic_array` - `Enum::clone_dynamic` → `Enum::to_dynamic_enum` - `List::clone_dynamic` → `List::to_dynamic_list` - `Map::clone_dynamic` → `Map::to_dynamic_map` - `Set::clone_dynamic` → `Set::to_dynamic_set` - `Struct::clone_dynamic` → `Struct::to_dynamic_struct` - `Tuple::clone_dynamic` → `Tuple::to_dynamic_tuple` - `TupleStruct::clone_dynamic` → `TupleStruct::to_dynamic_tuple_struct` |
||
![]() |
c3ff6d4136
|
Fix non-crate typos (#18219)
# Objective Correct spelling ## Solution Fix typos, specifically ones that I found in folders other than /crates ## Testing CI --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
ed7b366b24
|
Deprecate insert_or_spawn function family (#18147)
# Objective Based on #18054, this PR builds on #18035 to deprecate: - `Commands::insert_or_spawn_batch` - `Entities::alloc_at_without_replacement` - `Entities::alloc_at` - `World::insert_or_spawn_batch` - `World::insert_or_spawn_batch_with_caller` ## Testing Just deprecation, so no new tests. Note that as of writing #18035 is still under testing and review. ## Open Questions - [x] Should `entity::AllocAtWithoutReplacement` be deprecated? It is internal and only used in `Entities::alloc_at_without_replacement`. **EDIT:** Now deprecated. ## Migration Guide The following functions have been deprecated: - `Commands::insert_or_spawn_batch` - `World::insert_or_spawn_batch` - `World::insert_or_spawn_batch_with_caller` These functions, when used incorrectly, can cause major performance problems and are generally viewed as anti-patterns and foot guns. These are planned to be removed altogether in 0.17. Instead of these functions consider doing one of the following: Option A) Instead of despawing entities and re-spawning them at a particular id, insert the new `Disabled` component without despawning the entity, and use `try_insert_batch` or `insert_batch` and remove `Disabled` instead of re-spawning it. Option B) Instead of giving special meaning to an entity id, simply use `spawn_batch` and ensure entity references are valid when despawning. --------- Co-authored-by: JaySpruce <jsprucebruce@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
a530c07bc5
|
Preserve spawned RelationshipTarget order and other improvements (#17858)
Fixes #17720 ## Objective Spawning RelationshipTargets from scenes currently fails to preserve RelationshipTarget ordering (ex: `Children` has an arbitrary order). This is because it uses the normal hook flow to set up the collection, which means we are pushing onto the collection in _spawn order_ (which is currently in archetype order, which will often produce mismatched orderings). We need to preserve the ordering in the original RelationshipTarget collection. Ideally without expensive checking / fixups. ## Solution One solution would be to spawn in hierarchy-order. However this gets complicated as there can be multiple hierarchies, and it also means we can't spawn in more cache-friendly orders (ex: the current per-archetype spawning, or future even-smarter per-table spawning). Additionally, same-world cloning has _slightly_ more nuanced needs (ex: recursively clone linked relationships, while maintaining _original_ relationships outside of the tree via normal hooks). The preferred approach is to directly spawn the remapped RelationshipTarget collection, as this trivially preserves the ordering. Unfortunately we can't _just_ do that, as when we spawn the children with their Relationships (ex: `ChildOf`), that will insert a duplicate. We could "fixup" the collection retroactively by just removing the back half of duplicates, but this requires another pass / more lookups / allocating twice as much space. Additionally, it becomes complicated because observers could insert additional children, making it harder (aka more expensive) to determine which children are dupes and which are not. The path I chose is to support "opting out" of the relationship target hook in the contexts that need that, as this allows us to just cheaply clone the mapped collection. The relationship hook can look for this configuration when it runs and skip its logic when that happens. A "simple" / small-amount-of-code way to do this would be to add a "skip relationship spawn" flag to World. Sadly, any hook / observer that runs _as the result of an insert_ would also read this flag. We really need a way to scope this setting to a _specific_ insert. Therefore I opted to add a new `RelationshipInsertHookMode` enum and an `entity.insert_with_relationship_insert_hook_mode` variant. Obviously this is verbose and ugly. And nobody wants _more_ insert variants. But sadly this was the best I could come up with from a performance and capability perspective. If you have alternatives let me know! There are three variants: 1. `RelationshipInsertHookMode::Run`: always run relationship insert hooks (this is the default) 2. `RelationshipInsertHookMode::Skip`: do not run any relationship insert hooks for this insert (this is used by spawner code) 3. `RelationshipInsertHookMode::RunIfNotLinked`: only run hooks for _unlinked_ relationships (this is used in same-world recursive entity cloning to preserve relationships outside of the deep-cloned tree) Note that I have intentionally only added "insert with relationship hook mode" variants to the cases we absolutely need (everything else uses the default `Run` mode), just to keep the code size in check. I do not think we should add more without real _very necessary_ use cases. I also made some other minor tweaks: 1. I split out `SourceComponent` from `ComponentCloneCtx`. Reading the source component no longer needlessly blocks mutable access to `ComponentCloneCtx`. 2. Thanks to (1), I've removed the `RefCell` wrapper over the cloned component queue. 3. (1) also allowed me to write to the EntityMapper while queuing up clones, meaning we can reserve entities during the component clone and write them to the mapper _before_ inserting the component, meaning cloned collections can be mapped on insert. 4. I've removed the closure from `write_target_component_ptr` to simplify the API / make it compatible with the split `SourceComponent` approach. 5. I've renamed `EntityCloner::recursive` to `EntityCloner::linked_cloning` to connect that feature more directly with `RelationshipTarget::LINKED_SPAWN` 6. I've removed `EntityCloneBehavior::RelationshipTarget`. This was always intended to be temporary, and this new behavior removes the need for it. --------- Co-authored-by: Viktor Gustavsson <villor94@gmail.com> |
||
![]() |
ccb7069e7f
|
Change ChildOf to Childof { parent: Entity} and support deriving Relationship and RelationshipTarget with named structs (#17905)
# Objective fixes #17896 ## Solution Change ChildOf ( Entity ) to ChildOf { parent: Entity } by doing this we also allow users to use named structs for relationship derives, When you have more than 1 field in a struct with named fields the macro will look for a field with the attribute #[relationship] and all of the other fields should implement the Default trait. Unnamed fields are still supported. When u have a unnamed struct with more than one field the macro will fail. Do we want to support something like this ? ```rust #[derive(Component)] #[relationship_target(relationship = ChildOf)] pub struct Children (#[relationship] Entity, u8); ``` I could add this, it but doesn't seem nice. ## Testing crates/bevy_ecs - cargo test ## Showcase ```rust use bevy_ecs::component::Component; use bevy_ecs::entity::Entity; #[derive(Component)] #[relationship(relationship_target = Children)] pub struct ChildOf { #[relationship] pub parent: Entity, internal: u8, }; #[derive(Component)] #[relationship_target(relationship = ChildOf)] pub struct Children { children: Vec<Entity> }; ``` --------- Co-authored-by: Tim Overbeek <oorbecktim@Tims-MacBook-Pro.local> Co-authored-by: Tim Overbeek <oorbecktim@c-001-001-042.client.nl.eduvpn.org> Co-authored-by: Tim Overbeek <oorbecktim@c-001-001-059.client.nl.eduvpn.org> Co-authored-by: Tim Overbeek <oorbecktim@c-001-001-054.client.nl.eduvpn.org> Co-authored-by: Tim Overbeek <oorbecktim@c-001-001-027.client.nl.eduvpn.org> |
||
![]() |
c7531074bc
|
Improve cubic segment bezier functionality (#17645)
# Objective - Fixes #17642 ## Solution - Implemented method `new_bezier(points: [P; 4]) -> Self` for `CubicSegment<P>` - Old implementation of `new_bezier` is now `new_bezier_easing(p1: impl Into<Vec2>, p2: impl Into<Vec2>) -> Self` (**breaking change**) - ~~added method `new_bezier_with_anchor`, which can make a bezier curve between two points with one control anchor~~ - added methods `iter_positions`, `iter_velocities`, `iter_accelerations`, the same as in `CubicCurve` (**copied code, potentially can be reduced)** - bezier creation logic is moved from `CubicCurve` to `CubicSegment`, removing the unneeded allocation ## Testing - Did you test these changes? If so, how? - Run tests inside `crates/bevy_math/` - Tested the functionality in my project - Are there any parts that need more testing? - Did not run `cargo test` on the whole bevy directory because of OOM - Performance improvements are expected when creating `CubicCurve` with `new_bezier` and `new_bezier_easing`, but not tested - How can other people (reviewers) test your changes? Is there anything specific they need to know? - Use in any code that works created `CubicCurve::new_bezier` - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? - I don't think relevant --- ## Showcase ```rust // Imagine a car goes towards a local target // Create a simple `CubicSegment`, without using heap let planned_path = CubicSegment::new_bezier([ car_pos, car_pos + car_dir * turn_radius, target_point - target_dir * turn_radius, target_point, ]); // Check if the planned path itersect other entities for pos in planned_path.iter_positions(8) { // do some collision checks } ``` ## Migration Guide > This section is optional. If there are no breaking changes, you can delete this section. - Replace `CubicCurve::new_bezier` with `CubicCurve::new_bezier_easing` |
||
![]() |
20813aed64
|
Handle TriggerTargets that are combinations for components/entities (#18024)
# Objective * Fixes https://github.com/bevyengine/bevy/issues/14074 * Applies CI fixes for #16326 It is currently not possible to issues a trigger that targets a specific list of components AND a specific list of entities ## Solution We can now use `((A, B), (entity_1, entity_2))` as a trigger target, as well as the reverse ## Testing Added a unit test. The triggering rules for observers are quite confusing: Triggers once per entity target For each entity target, an observer system triggers if any of its components matches the trigger target components (but it triggers at most once, since we use an internal counter to make sure that an observer can run at most once per entity target) (copied from #14563) (copied from #16326) ## Notes All credit to @BenjaminBrienen and @cBournhonesque! Just applying a small fix to this PR so it can be merged. --------- Co-authored-by: Benjamin Brienen <Benjamin.Brienen@outlook.com> Co-authored-by: Christian Hughes <xdotdash@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
5241e09671
|
Upgrade to Rust Edition 2024 (#17967)
# Objective - Fixes #17960 ## Solution - Followed the [edition upgrade guide](https://doc.rust-lang.org/edition-guide/editions/transitioning-an-existing-project-to-a-new-edition.html) ## Testing - CI --- ## Summary of Changes ### Documentation Indentation When using lists in documentation, proper indentation is now linted for. This means subsequent lines within the same list item must start at the same indentation level as the item. ```rust /* Valid */ /// - Item 1 /// Run-on sentence. /// - Item 2 struct Foo; /* Invalid */ /// - Item 1 /// Run-on sentence. /// - Item 2 struct Foo; ``` ### Implicit `!` to `()` Conversion `!` (the never return type, returned by `panic!`, etc.) no longer implicitly converts to `()`. This is particularly painful for systems with `todo!` or `panic!` statements, as they will no longer be functions returning `()` (or `Result<()>`), making them invalid systems for functions like `add_systems`. The ideal fix would be to accept functions returning `!` (or rather, _not_ returning), but this is blocked on the [stabilisation of the `!` type itself](https://doc.rust-lang.org/std/primitive.never.html), which is not done. The "simple" fix would be to add an explicit `-> ()` to system signatures (e.g., `|| { todo!() }` becomes `|| -> () { todo!() }`). However, this is _also_ banned, as there is an existing lint which (IMO, incorrectly) marks this as an unnecessary annotation. So, the "fix" (read: workaround) is to put these kinds of `|| -> ! { ... }` closuers into variables and give the variable an explicit type (e.g., `fn()`). ```rust // Valid let system: fn() = || todo!("Not implemented yet!"); app.add_systems(..., system); // Invalid app.add_systems(..., || todo!("Not implemented yet!")); ``` ### Temporary Variable Lifetimes The order in which temporary variables are dropped has changed. The simple fix here is _usually_ to just assign temporaries to a named variable before use. ### `gen` is a keyword We can no longer use the name `gen` as it is reserved for a future generator syntax. This involved replacing uses of the name `gen` with `r#gen` (the raw-identifier syntax). ### Formatting has changed Use statements have had the order of imports changed, causing a substantial +/-3,000 diff when applied. For now, I have opted-out of this change by amending `rustfmt.toml` ```toml style_edition = "2021" ``` This preserves the original formatting for now, reducing the size of this PR. It would be a simple followup to update this to 2024 and run `cargo fmt`. ### New `use<>` Opt-Out Syntax Lifetimes are now implicitly included in RPIT types. There was a handful of instances where it needed to be added to satisfy the borrow checker, but there may be more cases where it _should_ be added to avoid breakages in user code. ### `MyUnitStruct { .. }` is an invalid pattern Previously, you could match against unit structs (and unit enum variants) with a `{ .. }` destructuring. This is no longer valid. ### Pretty much every use of `ref` and `mut` are gone Pattern binding has changed to the point where these terms are largely unused now. They still serve a purpose, but it is far more niche now. ### `iter::repeat(...).take(...)` is bad New lint recommends using the more explicit `iter::repeat_n(..., ...)` instead. ## Migration Guide The lifetimes of functions using return-position impl-trait (RPIT) are likely _more_ conservative than they had been previously. If you encounter lifetime issues with such a function, please create an issue to investigate the addition of `+ use<...>`. ## Notes - Check the individual commits for a clearer breakdown for what _actually_ changed. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
![]() |
63e0f794d1
|
Enable nonstandard_macro_braces and enforce [] for children! (#17974)
# Objective - [`nonstandard_macro_braces`](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces) is a Clippy lint that enforces what braces certain known macros are allowed to use. - For instance, requiring `println!()` instead of `println!{}`. - I started working on this after seeing https://github.com/TheBevyFlock/bevy_cli/issues/277. ## Solution - Enable `nonstandard_macro_braces` in the workspace. - Configure Clippy so it enforces `[]` braces for `children!`. ## Testing 1. Create `examples/clippy_test.rs`. 2. Paste the following code: ```rust //! Some docs woooooooo use bevy::prelude::*; fn main() { let _ = children!(Name::new("Foo")); } ``` 3. Run `cargo clippy --example clippy_test`. 4. Ensure the following warning is emitted: ```sh warning: use of irregular braces for `children!` macro --> examples/clippy_test.rs:6:13 | 6 | let _ = children!(Name::new("Foo")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `children![Name::new("Foo")]` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces = note: requested on the command line with `-W clippy::nonstandard-macro-braces` warning: `bevy` (example "clippy_test") generated 1 warning (run `cargo clippy --fix --example "clippy_test"` to apply 1 suggestion) ``` |
||
![]() |
ea578415e1
|
Improved Spawn APIs and Bundle Effects (#17521)
## Objective A major critique of Bevy at the moment is how boilerplatey it is to compose (and read) entity hierarchies: ```rust commands .spawn(Foo) .with_children(|p| { p.spawn(Bar).with_children(|p| { p.spawn(Baz); }); p.spawn(Bar).with_children(|p| { p.spawn(Baz); }); }); ``` There is also currently no good way to statically define and return an entity hierarchy from a function. Instead, people often do this "internally" with a Commands function that returns nothing, making it impossible to spawn the hierarchy in other cases (direct World spawns, ChildSpawner, etc). Additionally, because this style of API results in creating the hierarchy bits _after_ the initial spawn of a bundle, it causes ECS archetype changes (and often expensive table moves). Because children are initialized after the fact, we also can't count them to pre-allocate space. This means each time a child inserts itself, it has a high chance of overflowing the currently allocated capacity in the `RelationshipTarget` collection, causing literal worst-case reallocations. We can do better! ## Solution The Bundle trait has been extended to support an optional `BundleEffect`. This is applied directly to World immediately _after_ the Bundle has fully inserted. Note that this is [intentionally](https://github.com/bevyengine/bevy/discussions/16920) _not done via a deferred Command_, which would require repeatedly copying each remaining subtree of the hierarchy to a new command as we walk down the tree (_not_ good performance). This allows us to implement the new `SpawnRelated` trait for all `RelationshipTarget` impls, which looks like this in practice: ```rust world.spawn(( Foo, Children::spawn(( Spawn(( Bar, Children::spawn(Spawn(Baz)), )), Spawn(( Bar, Children::spawn(Spawn(Baz)), )), )) )) ``` `Children::spawn` returns `SpawnRelatedBundle<Children, L: SpawnableList>`, which is a `Bundle` that inserts `Children` (preallocated to the size of the `SpawnableList::size_hint()`). `Spawn<B: Bundle>(pub B)` implements `SpawnableList` with a size of 1. `SpawnableList` is also implemented for tuples of `SpawnableList` (same general pattern as the Bundle impl). There are currently three built-in `SpawnableList` implementations: ```rust world.spawn(( Foo, Children::spawn(( Spawn(Name::new("Child1")), SpawnIter(["Child2", "Child3"].into_iter().map(Name::new), SpawnWith(|parent: &mut ChildSpawner| { parent.spawn(Name::new("Child4")); parent.spawn(Name::new("Child5")); }) )), )) ``` We get the benefits of "structured init", but we have nice flexibility where it is required! Some readers' first instinct might be to try to remove the need for the `Spawn` wrapper. This is impossible in the Rust type system, as a tuple of "child Bundles to be spawned" and a "tuple of Components to be added via a single Bundle" is ambiguous in the Rust type system. There are two ways to resolve that ambiguity: 1. By adding support for variadics to the Rust type system (removing the need for nested bundles). This is out of scope for this PR :) 2. Using wrapper types to resolve the ambiguity (this is what I did in this PR). For the single-entity spawn cases, `Children::spawn_one` does also exist, which removes the need for the wrapper: ```rust world.spawn(( Foo, Children::spawn_one(Bar), )) ``` ## This works for all Relationships This API isn't just for `Children` / `ChildOf` relationships. It works for any relationship type, and they can be mixed and matched! ```rust world.spawn(( Foo, Observers::spawn(( Spawn(Observer::new(|trigger: Trigger<FuseLit>| {})), Spawn(Observer::new(|trigger: Trigger<Exploded>| {})), )), OwnerOf::spawn(Spawn(Bar)) Children::spawn(Spawn(Baz)) )) ``` ## Macros While `Spawn` is necessary to satisfy the type system, we _can_ remove the need to express it via macros. The example above can be expressed more succinctly using the new `children![X]` macro, which internally produces `Children::spawn(Spawn(X))`: ```rust world.spawn(( Foo, children![ ( Bar, children![Baz], ), ( Bar, children![Baz], ), ] )) ``` There is also a `related!` macro, which is a generic version of the `children!` macro that supports any relationship type: ```rust world.spawn(( Foo, related!(Children[ ( Bar, related!(Children[Baz]), ), ( Bar, related!(Children[Baz]), ), ]) )) ``` ## Returning Hierarchies from Functions Thanks to these changes, the following pattern is now possible: ```rust fn button(text: &str, color: Color) -> impl Bundle { ( Node { width: Val::Px(300.), height: Val::Px(100.), ..default() }, BackgroundColor(color), children![ Text::new(text), ] ) } fn ui() -> impl Bundle { ( Node { width: Val::Percent(100.0), height: Val::Percent(100.0), ..default(), }, children![ button("hello", BLUE), button("world", RED), ] ) } // spawn from a system fn system(mut commands: Commands) { commands.spawn(ui()); } // spawn directly on World world.spawn(ui()); ``` ## Additional Changes and Notes * `Bundle::from_components` has been split out into `BundleFromComponents::from_components`, enabling us to implement `Bundle` for types that cannot be "taken" from the ECS (such as the new `SpawnRelatedBundle`). * The `NoBundleEffect` trait (which implements `BundleEffect`) is implemented for empty tuples (and tuples of empty tuples), which allows us to constrain APIs to only accept bundles that do not have effects. This is critical because the current batch spawn APIs cannot efficiently apply BundleEffects in their current form (as doing so in-place could invalidate the cached raw pointers). We could consider allocating a buffer of the effects to be applied later, but that does have performance implications that could offset the balance and value of the batched APIs (and would likely require some refactors to the underlying code). I've decided to be conservative here. We can consider relaxing that requirement on those APIs later, but that should be done in a followup imo. * I've ported a few examples to illustrate real-world usage. I think in a followup we should port all examples to the `children!` form whenever possible (and for cases that require things like SpawnIter, use the raw APIs). * Some may ask "why not use the `Relationship` to spawn (ex: `ChildOf::spawn(Foo)`) instead of the `RelationshipTarget` (ex: `Children::spawn(Spawn(Foo))`)?". That _would_ allow us to remove the `Spawn` wrapper. I've explicitly chosen to disallow this pattern. `Bundle::Effect` has the ability to create _significant_ weirdness. Things in `Bundle` position look like components. For example `world.spawn((Foo, ChildOf::spawn(Bar)))` _looks and reads_ like Foo is a child of Bar. `ChildOf` is in Foo's "component position" but it is not a component on Foo. This is a huge problem. Now that `Bundle::Effect` exists, we should be _very_ principled about keeping the "weird and unintuitive behavior" to a minimum. Things that read like components _should be the components they appear to be". ## Remaining Work * The macros are currently trivially implemented using macro_rules and are currently limited to the max tuple length. They will require a proc_macro implementation to work around the tuple length limit. ## Next Steps * Port the remaining examples to use `children!` where possible and raw `Spawn` / `SpawnIter` / `SpawnWith` where the flexibility of the raw API is required. ## Migration Guide Existing spawn patterns will continue to work as expected. Manual Bundle implementations now require a `BundleEffect` associated type. Exisiting bundles would have no bundle effect, so use `()`. Additionally `Bundle::from_components` has been moved to the new `BundleFromComponents` trait. ```rust // Before unsafe impl Bundle for X { unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self { } /* remaining bundle impl here */ } // After unsafe impl Bundle for X { type Effect = (); /* remaining bundle impl here */ } unsafe impl BundleFromComponents for X { unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self { } } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Co-authored-by: Emerson Coskey <emerson@coskey.dev> |
||
![]() |
1b7db895b7
|
Harden proc macro path resolution and add integration tests. (#17330)
This pr uses the `extern crate self as` trick to make proc macros behave the same way inside and outside bevy. # Objective - Removes noise introduced by `crate as` in the whole bevy repo. - Fixes #17004. - Hardens proc macro path resolution. ## TODO - [x] `BevyManifest` needs cleanup. - [x] Cleanup remaining `crate as`. - [x] Add proper integration tests to the ci. ## Notes - `cargo-manifest-proc-macros` is written by me and based/inspired by the old `BevyManifest` implementation and [`bkchr/proc-macro-crate`](https://github.com/bkchr/proc-macro-crate). - What do you think about the new integration test machinery I added to the `ci`? More and better integration tests can be added at a later stage. The goal of these integration tests is to simulate an actual separate crate that uses bevy. Ideally they would lightly touch all bevy crates. ## Testing - Needs RA test - Needs testing from other users - Others need to run at least `cargo run -p ci integration-test` and verify that they work. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
3c8fae2390
|
Improved Entity Mapping and Cloning (#17687)
Fixes #17535 Bevy's approach to handling "entity mapping" during spawning and cloning needs some work. The addition of [Relations](https://github.com/bevyengine/bevy/pull/17398) both [introduced a new "duplicate entities" bug when spawning scenes in the scene system](#17535) and made the weaknesses of the current mapping system exceedingly clear: 1. Entity mapping requires _a ton_ of boilerplate (implement or derive VisitEntities and VisitEntitesMut, then register / reflect MapEntities). Knowing the incantation is challenging and if you forget to do it in part or in whole, spawning subtly breaks. 2. Entity mapping a spawned component in scenes incurs unnecessary overhead: look up ReflectMapEntities, create a _brand new temporary instance_ of the component using FromReflect, map the entities in that instance, and then apply that on top of the actual component using reflection. We can do much better. Additionally, while our new [Entity cloning system](https://github.com/bevyengine/bevy/pull/16132) is already pretty great, it has some areas we can make better: * It doesn't expose semantic info about the clone (ex: ignore or "clone empty"), meaning we can't key off of that in places where it would be useful, such as scene spawning. Rather than duplicating this info across contexts, I think it makes more sense to add that info to the clone system, especially given that we'd like to use cloning code in some of our spawning scenarios. * EntityCloner is currently built in a way that prioritizes a single entity clone * EntityCloner's recursive cloning is built to be done "inside out" in a parallel context (queue commands that each have a clone of EntityCloner). By making EntityCloner the orchestrator of the clone we can remove internal arcs, improve the clarity of the code, make EntityCloner mutable again, and simplify the builder code. * EntityCloner does not currently take into account entity mapping. This is necessary to do true "bullet proof" cloning, would allow us to unify the per-component scene spawning and cloning UX, and ultimately would allow us to use EntityCloner in place of raw reflection for scenes like `Scene(World)` (which would give us a nice performance boost: fewer archetype moves, less reflection overhead). ## Solution ### Improved Entity Mapping First, components now have first-class "entity visiting and mapping" behavior: ```rust #[derive(Component, Reflect)] #[reflect(Component)] struct Inventory { size: usize, #[entities] items: Vec<Entity>, } ``` Any field with the `#[entities]` annotation will be viewable and mappable when cloning and spawning scenes. Compare that to what was required before! ```rust #[derive(Component, Reflect, VisitEntities, VisitEntitiesMut)] #[reflect(Component, MapEntities)] struct Inventory { #[visit_entities(ignore)] size: usize, items: Vec<Entity>, } ``` Additionally, for relationships `#[entities]` is implied, meaning this "just works" in scenes and cloning: ```rust #[derive(Component, Reflect)] #[relationship(relationship_target = Children)] #[reflect(Component)] struct ChildOf(pub Entity); ``` Note that Component _does not_ implement `VisitEntities` directly. Instead, it has `Component::visit_entities` and `Component::visit_entities_mut` methods. This is for a few reasons: 1. We cannot implement `VisitEntities for C: Component` because that would conflict with our impl of VisitEntities for anything that implements `IntoIterator<Item=Entity>`. Preserving that impl is more important from a UX perspective. 2. We should not implement `Component: VisitEntities` VisitEntities in the Component derive, as that would increase the burden of manual Component trait implementors. 3. Making VisitEntitiesMut directly callable for components would make it easy to invalidate invariants defined by a component author. By putting it in the `Component` impl, we can make it harder to call naturally / unavailable to autocomplete using `fn visit_entities_mut(this: &mut Self, ...)`. `ReflectComponent::apply_or_insert` is now `ReflectComponent::apply_or_insert_mapped`. By moving mapping inside this impl, we remove the need to go through the reflection system to do entity mapping, meaning we no longer need to create a clone of the target component, map the entities in that component, and patch those values on top. This will make spawning mapped entities _much_ faster (The default `Component::visit_entities_mut` impl is an inlined empty function, so it will incur no overhead for unmapped entities). ### The Bug Fix To solve #17535, spawning code now skips entities with the new `ComponentCloneBehavior::Ignore` and `ComponentCloneBehavior::RelationshipTarget` variants (note RelationshipTarget is a temporary "workaround" variant that allows scenes to skip these components. This is a temporary workaround that can be removed as these cases should _really_ be using EntityCloner logic, which should be done in a followup PR. When that is done, `ComponentCloneBehavior::RelationshipTarget` can be merged into the normal `ComponentCloneBehavior::Custom`). ### Improved Cloning * `Option<ComponentCloneHandler>` has been replaced by `ComponentCloneBehavior`, which encodes additional intent and context (ex: `Default`, `Ignore`, `Custom`, `RelationshipTarget` (this last one is temporary)). * Global per-world entity cloning configuration has been removed. This felt overly complicated, increased our API surface, and felt too generic. Each clone context can have different requirements (ex: what a user wants in a specific system, what a scene spawner wants, etc). I'd prefer to see how far context-specific EntityCloners get us first. * EntityCloner's internals have been reworked to remove Arcs and make it mutable. * EntityCloner is now directly stored on EntityClonerBuilder, simplifying the code somewhat * EntityCloner's "bundle scratch" pattern has been moved into the new BundleScratch type, improving its usability and making it usable in other contexts (such as future cross-world cloning code). Currently this is still private, but with some higher level safe APIs it could be used externally for making dynamic bundles * EntityCloner's recursive cloning behavior has been "externalized". It is now responsible for orchestrating recursive clones, meaning it no longer needs to be sharable/clone-able across threads / read-only. * EntityCloner now does entity mapping during clones, like scenes do. This gives behavior parity and also makes it more generically useful. * `RelatonshipTarget::RECURSIVE_SPAWN` is now `RelationshipTarget::LINKED_SPAWN`, and this field is used when cloning relationship targets to determine if cloning should happen recursively. The new `LINKED_SPAWN` term was picked to make it more generically applicable across spawning and cloning scenarios. ## Next Steps * I think we should adapt EntityCloner to support cross world cloning. I think this PR helps set the stage for that by making the internals slightly more generalized. We could have a CrossWorldEntityCloner that reuses a lot of this infrastructure. * Once we support cross world cloning, we should use EntityCloner to spawn `Scene(World)` scenes. This would yield significant performance benefits (no archetype moves, less reflection overhead). --------- Co-authored-by: eugineerd <70062110+eugineerd@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
15f00278e7
|
Rename ArgList::push methods to with and add new push methods which take &mut self (#16567)
# Objective The `ArgList::push` family of methods consume `self` and return a new `ArgList` which means they can't be used with `&mut ArgList` references. ```rust fn foo(args: &mut ArgList) { args.push_owned(47_i32); // doesn't work :( } ``` It's typical for `push` methods on other existing types to take `&mut self`. ## Solution Renamed the existing push methods to `with_arg`, `with_ref` etc and added new `push` methods which take `&mut self`. ## Migration Guide Uses of the `ArgList::push` methods should be replaced with the `with` counterpart. <details> | old | new | | --- | --- | | push_arg | with_arg | | push_ref | with_ref | | push_mut | with_mut | | push_owned | with_owned | | push_boxed | with_boxed | </details> |
||
![]() |
9bc0ae33c3
|
Move hashbrown and foldhash out of bevy_utils (#17460)
# Objective - Contributes to #16877 ## Solution - Moved `hashbrown`, `foldhash`, and related types out of `bevy_utils` and into `bevy_platform_support` - Refactored the above to match the layout of these types in `std`. - Updated crates as required. ## Testing - CI --- ## Migration Guide - The following items were moved out of `bevy_utils` and into `bevy_platform_support::hash`: - `FixedState` - `DefaultHasher` - `RandomState` - `FixedHasher` - `Hashed` - `PassHash` - `PassHasher` - `NoOpHash` - The following items were moved out of `bevy_utils` and into `bevy_platform_support::collections`: - `HashMap` - `HashSet` - `bevy_utils::hashbrown` has been removed. Instead, import from `bevy_platform_support::collections` _or_ take a dependency on `hashbrown` directly. - `bevy_utils::Entry` has been removed. Instead, import from `bevy_platform_support::collections::hash_map` or `bevy_platform_support::collections::hash_set` as appropriate. - All of the above equally apply to `bevy::utils` and `bevy::platform_support`. ## Notes - I left `PreHashMap`, `PreHashMapExt`, and `TypeIdMap` in `bevy_utils` as they might be candidates for micro-crating. They can always be moved into `bevy_platform_support` at a later date if desired. |
||
![]() |
ba5e71f53d
|
Parent -> ChildOf (#17427)
Fixes #17412 ## Objective `Parent` uses the "has a X" naming convention. There is increasing sentiment that we should use the "is a X" naming convention for relationships (following #17398). This leaves `Children` as-is because there is prevailing sentiment that `Children` is clearer than `ParentOf` in many cases (especially when treating it like a collection). This renames `Parent` to `ChildOf`. This is just the implementation PR. To discuss the path forward, do so in #17412. ## Migration Guide - The `Parent` component has been renamed to `ChildOf`. |
||
![]() |
5a9bc28502
|
Support non-Vec data structures in relations (#17447)
# Objective
The existing `RelationshipSourceCollection` uses `Vec` as the only
possible backing for our relationships. While a reasonable choice,
benchmarking use cases might reveal that a different data type is better
or faster.
For example:
- Not all relationships require a stable ordering between the
relationship sources (i.e. children). In cases where we a) have many
such relations and b) don't care about the ordering between them, a hash
set is likely a better datastructure than a `Vec`.
- The number of children-like entities may be small on average, and a
`smallvec` may be faster
## Solution
- Implement `RelationshipSourceCollection` for `EntityHashSet`, our
custom entity-optimized `HashSet`.
-~~Implement `DoubleEndedIterator` for `EntityHashSet` to make things
compile.~~
- This implementation was cursed and very surprising.
- Instead, by moving the iterator type on `RelationshipSourceCollection`
from an erased RPTIT to an explicit associated type we can add a trait
bound on the offending methods!
- Implement `RelationshipSourceCollection` for `SmallVec`
## Testing
I've added a pair of new tests to make sure this pattern compiles
successfully in practice!
## Migration Guide
`EntityHashSet` and `EntityHashMap` are no longer re-exported in
`bevy_ecs::entity` directly. If you were not using `bevy_ecs` / `bevy`'s
`prelude`, you can access them through their now-public modules,
`hash_set` and `hash_map` instead.
## Notes to reviewers
The `EntityHashSet::Iter` type needs to be public for this impl to be
allowed. I initially renamed it to something that wasn't ambiguous and
re-exported it, but as @Victoronz pointed out, that was somewhat
unidiomatic.
In
|
||
![]() |
21f1e3045c
|
Relationships (non-fragmenting, one-to-many) (#17398)
This adds support for one-to-many non-fragmenting relationships (with planned paths for fragmenting and non-fragmenting many-to-many relationships). "Non-fragmenting" means that entities with the same relationship type, but different relationship targets, are not forced into separate tables (which would cause "table fragmentation"). Functionally, this fills a similar niche as the current Parent/Children system. The biggest differences are: 1. Relationships have simpler internals and significantly improved performance and UX. Commands and specialized APIs are no longer necessary to keep everything in sync. Just spawn entities with the relationship components you want and everything "just works". 2. Relationships are generalized. Bevy can provide additional built in relationships, and users can define their own. **REQUEST TO REVIEWERS**: _please don't leave top level comments and instead comment on specific lines of code. That way we can take advantage of threaded discussions. Also dont leave comments simply pointing out CI failures as I can read those just fine._ ## Built on top of what we have Relationships are implemented on top of the Bevy ECS features we already have: components, immutability, and hooks. This makes them immediately compatible with all of our existing (and future) APIs for querying, spawning, removing, scenes, reflection, etc. The fewer specialized APIs we need to build, maintain, and teach, the better. ## Why focus on one-to-many non-fragmenting first? 1. This allows us to improve Parent/Children relationships immediately, in a way that is reasonably uncontroversial. Switching our hierarchy to fragmenting relationships would have significant performance implications. ~~Flecs is heavily considering a switch to non-fragmenting relations after careful considerations of the performance tradeoffs.~~ _(Correction from @SanderMertens: Flecs is implementing non-fragmenting storage specialized for asset hierarchies, where asset hierarchies are many instances of small trees that have a well defined structure)_ 2. Adding generalized one-to-many relationships is currently a priority for the [Next Generation Scene / UI effort](https://github.com/bevyengine/bevy/discussions/14437). Specifically, we're interested in building reactions and observers on top. ## The changes This PR does the following: 1. Adds a generic one-to-many Relationship system 3. Ports the existing Parent/Children system to Relationships, which now lives in `bevy_ecs::hierarchy`. The old `bevy_hierarchy` crate has been removed. 4. Adds on_despawn component hooks 5. Relationships can opt-in to "despawn descendants" behavior, meaning that the entire relationship hierarchy is despawned when `entity.despawn()` is called. The built in Parent/Children hierarchies enable this behavior, and `entity.despawn_recursive()` has been removed. 6. `world.spawn` now applies commands after spawning. This ensures that relationship bookkeeping happens immediately and removes the need to manually flush. This is in line with the equivalent behaviors recently added to the other APIs (ex: insert). 7. Removes the ValidParentCheckPlugin (system-driven / poll based) in favor of a `validate_parent_has_component` hook. ## Using Relationships The `Relationship` trait looks like this: ```rust pub trait Relationship: Component + Sized { type RelationshipSources: RelationshipSources<Relationship = Self>; fn get(&self) -> Entity; fn from(entity: Entity) -> Self; } ``` A relationship is a component that: 1. Is a simple wrapper over a "target" Entity. 2. Has a corresponding `RelationshipSources` component, which is a simple wrapper over a collection of entities. Every "target entity" targeted by a "source entity" with a `Relationship` has a `RelationshipSources` component, which contains every "source entity" that targets it. For example, the `Parent` component (as it currently exists in Bevy) is the `Relationship` component and the entity containing the Parent is the "source entity". The entity _inside_ the `Parent(Entity)` component is the "target entity". And that target entity has a `Children` component (which implements `RelationshipSources`). In practice, the Parent/Children relationship looks like this: ```rust #[derive(Relationship)] #[relationship(relationship_sources = Children)] pub struct Parent(pub Entity); #[derive(RelationshipSources)] #[relationship_sources(relationship = Parent)] pub struct Children(Vec<Entity>); ``` The Relationship and RelationshipSources derives automatically implement Component with the relevant configuration (namely, the hooks necessary to keep everything in sync). The most direct way to add relationships is to spawn entities with relationship components: ```rust let a = world.spawn_empty().id(); let b = world.spawn(Parent(a)).id(); assert_eq!(world.entity(a).get::<Children>().unwrap(), &[b]); ``` There are also convenience APIs for spawning more than one entity with the same relationship: ```rust world.spawn_empty().with_related::<Children>(|s| { s.spawn_empty(); s.spawn_empty(); }) ``` The existing `with_children` API is now a simpler wrapper over `with_related`. This makes this change largely non-breaking for existing spawn patterns. ```rust world.spawn_empty().with_children(|s| { s.spawn_empty(); s.spawn_empty(); }) ``` There are also other relationship APIs, such as `add_related` and `despawn_related`. ## Automatic recursive despawn via the new on_despawn hook `RelationshipSources` can opt-in to "despawn descendants" behavior, which will despawn all related entities in the relationship hierarchy: ```rust #[derive(RelationshipSources)] #[relationship_sources(relationship = Parent, despawn_descendants)] pub struct Children(Vec<Entity>); ``` This means that `entity.despawn_recursive()` is no longer required. Instead, just use `entity.despawn()` and the relevant related entities will also be despawned. To despawn an entity _without_ despawning its parent/child descendants, you should remove the `Children` component first, which will also remove the related `Parent` components: ```rust entity .remove::<Children>() .despawn() ``` This builds on the on_despawn hook introduced in this PR, which is fired when an entity is despawned (before other hooks). ## Relationships are the source of truth `Relationship` is the _single_ source of truth component. `RelationshipSources` is merely a reflection of what all the `Relationship` components say. By embracing this, we are able to significantly improve the performance of the system as a whole. We can rely on component lifecycles to protect us against duplicates, rather than needing to scan at runtime to ensure entities don't already exist (which results in quadratic runtime). A single source of truth gives us constant-time inserts. This does mean that we cannot directly spawn populated `Children` components (or directly add or remove entities from those components). I personally think this is a worthwhile tradeoff, both because it makes the performance much better _and_ because it means theres exactly one way to do things (which is a philosophy we try to employ for Bevy APIs). As an aside: treating both sides of the relationship as "equivalent source of truth relations" does enable building simple and flexible many-to-many relationships. But this introduces an _inherent_ need to scan (or hash) to protect against duplicates. [`evergreen_relations`](https://github.com/EvergreenNest/evergreen_relations) has a very nice implementation of the "symmetrical many-to-many" approach. Unfortunately I think the performance issues inherent to that approach make it a poor choice for Bevy's default relationship system. ## Followup Work * Discuss renaming `Parent` to `ChildOf`. I refrained from doing that in this PR to keep the diff reasonable, but I'm personally biased toward this change (and using that naming pattern generally for relationships). * [Improved spawning ergonomics](https://github.com/bevyengine/bevy/discussions/16920) * Consider adding relationship observers/triggers for "relationship targets" whenever a source is added or removed. This would replace the current "hierarchy events" system, which is unused upstream but may have existing users downstream. I think triggers are the better fit for this than a buffered event queue, and would prefer not to add that back. * Fragmenting relations: My current idea hinges on the introduction of "value components" (aka: components whose type _and_ value determines their ComponentId, via something like Hashing / PartialEq). By labeling a Relationship component such as `ChildOf(Entity)` as a "value component", `ChildOf(e1)` and `ChildOf(e2)` would be considered "different components". This makes the transition between fragmenting and non-fragmenting a single flag, and everything else continues to work as expected. * Many-to-many support * Non-fragmenting: We can expand Relationship to be a list of entities instead of a single entity. I have largely already written the code for this. * Fragmenting: With the "value component" impl mentioned above, we get many-to-many support "for free", as it would allow inserting multiple copies of a Relationship component with different target entities. Fixes #3742 (If this PR is merged, I think we should open more targeted followup issues for the work above, with a fresh tracking issue free of the large amount of less-directed historical context) Fixes #17301 Fixes #12235 Fixes #15299 Fixes #15308 ## Migration Guide * Replace `ChildBuilder` with `ChildSpawnerCommands`. * Replace calls to `.set_parent(parent_id)` with `.insert(Parent(parent_id))`. * Replace calls to `.replace_children()` with `.remove::<Children>()` followed by `.add_children()`. Note that you'll need to manually despawn any children that are not carried over. * Replace calls to `.despawn_recursive()` with `.despawn()`. * Replace calls to `.despawn_descendants()` with `.despawn_related::<Children>()`. * If you have any calls to `.despawn()` which depend on the children being preserved, you'll need to remove the `Children` component first. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
26bb0b40d2
|
Move #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] to the workspace Cargo.toml (#17374)
# Objective Fixes https://github.com/bevyengine/bevy/issues/17111 ## Solution Move `#![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)]` to the workspace `Cargo.toml` ## Testing Lots of CI testing, and local testing too. --------- Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com> |
||
![]() |
4bca7f1b6d
|
Improved Command Errors (#17215)
# Objective Rework / build on #17043 to simplify the implementation. #17043 should be merged first, and the diff from this PR will get much nicer after it is merged (this PR is net negative LOC). ## Solution 1. Command and EntityCommand have been vastly simplified. No more marker components. Just one function. 2. Command and EntityCommand are now generic on the return type. This enables result-less commands to exist, and allows us to statically distinguish between fallible and infallible commands, which allows us to skip the "error handling overhead" for cases that don't need it. 3. There are now only two command queue variants: `queue` and `queue_fallible`. `queue` accepts commands with no return type. `queue_fallible` accepts commands that return a Result (specifically, one that returns an error that can convert to `bevy_ecs::result::Error`). 4. I've added the concept of the "default error handler", which is used by `queue_fallible`. This is a simple direct call to the `panic()` error handler by default. Users that want to override this can enable the `configurable_error_handler` cargo feature, then initialize the GLOBAL_ERROR_HANDLER value on startup. This is behind a flag because there might be minor overhead with `OnceLock` and I'm guessing this will be a niche feature. We can also do perf testing with OnceLock if someone really wants it to be used unconditionally, but I don't personally feel the need to do that. 5. I removed the "temporary error handler" on Commands (and all code associated with it). It added more branching, made Commands bigger / more expensive to initialize (note that we construct it at high frequencies / treat it like a pointer type), made the code harder to follow, and introduced a bunch of additional functions. We instead rely on the new default error handler used in `queue_fallible` for most things. In the event that a custom handler is required, `handle_error_with` can be used. 6. EntityCommand now _only_ supports functions that take `EntityWorldMut` (and all existing entity commands have been ported). Removing the marker component from EntityCommand hinged on this change, but I strongly believe this is for the best anyway, as this sets the stage for more efficient batched entity commands. 7. I added `EntityWorldMut::resource` and the other variants for more ergonomic resource access on `EntityWorldMut` (removes the need for entity.world_scope, which also incurs entity-lookup overhead). ## Open Questions 1. I believe we could merge `queue` and `queue_fallible` into a single `queue` which accepts both fallible and infallible commands (via the introduction of a `QueueCommand` trait). Is this desirable? |
||
![]() |
3742e621ef
|
Allow clippy::too_many_arguments to lint without warnings (#17249)
# Objective Many instances of `clippy::too_many_arguments` linting happen to be on systems - functions which we don't call manually, and thus there's not much reason to worry about the argument count. ## Solution Allow `clippy::too_many_arguments` globally, and remove all lint attributes related to it. |
||
![]() |
8e51b326b5
|
Cleanup instances of #[allow(clippy::type_complexity)] (#17248)
# Objective I never realized `clippy::type_complexity` was an allowed lint - I've been assuming it'd generate a warning when performing my linting PRs. ## Solution Removes any instances of `#[allow(clippy::type_complexity)]` and `#[expect(clippy::type_complexity)]` ## Testing `cargo clippy` ran without errors or warnings. |
||
![]() |
020d082617
|
Fix "Unrecognized Option" error when using Criterion-specific arguments in benchmarks (#17222)
# Objective - Commands like `cargo bench -- --save-baseline before` do not work because the default `libtest` is intercepting Criterion-specific CLI arguments. - Fixes #17200. ## Solution - Disable the default `libtest` benchmark harness for the library crate, as per [the Criterion book](https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options). ## Testing - `cargo bench -p benches -- --save-baseline before` - You don't need to run the entire benchmarks, just make sure that they start without any errors. :) |
||
![]() |
ee4414159b
|
Add Result handling to Commands and EntityCommands (#17043)
## Objective Fixes #2004 Fixes #3845 Fixes #7118 Fixes #10166 ## Solution - The crux of this PR is the new `Command::with_error_handling` method. This wraps the relevant command in another command that, when applied, will apply the original command and handle any resulting errors. - To enable this, `Command::apply` and `EntityCommand::apply` now return `Result`. - `Command::with_error_handling` takes as a parameter an error handler of the form `fn(&mut World, CommandError)`, which it passes the error to. - `CommandError` is an enum that can be either `NoSuchEntity(Entity)` or `CommandFailed(Box<dyn Error>)`. ### Closures - Closure commands can now optionally return `Result`, which will be passed to `with_error_handling`. ### Commands - Fallible commands can be queued with `Commands::queue_fallible` and `Commands::queue_fallible_with`, which call `with_error_handling` before queuing them (using `Commands::queue` will queue them without error handling). - `Commands::queue_fallible_with` takes an `error_handler` parameter, which will be used by `with_error_handling` instead of a command's default. - The `command` submodule provides unqueued forms of built-in fallible commands so that you can use them with `queue_fallible_with`. - There is also an `error_handler` submodule that provides simple error handlers for convenience. ### Entity Commands - `EntityCommand` now automatically checks if the entity exists before executing the command, and returns `NoSuchEntity` if it doesn't. - Since all entity commands might need to return an error, they are always queued with error handling. - `EntityCommands::queue_with` takes an `error_handler` parameter, which will be used by `with_error_handling` instead of a command's default. - The `entity_command` submodule provides unqueued forms of built-in entity commands so that you can use them with `queue_with`. ### Defaults - In the future, commands should all fail according to the global error handling setting. That doesn't exist yet though. - For this PR, commands all fail the way they do on `main`. - Both now and in the future, the defaults can be overridden by `Commands::override_error_handler` (or equivalent methods on `EntityCommands` and `EntityEntryCommands`). - `override_error_handler` takes an error handler (`fn(&mut World, CommandError)`) and passes it to every subsequent command queued with `Commands::queue_fallible` or `EntityCommands::queue`. - The `_with` variants of the queue methods will still provide an error handler directly to the command. - An override can be reset with `reset_error_handler`. ## Future Work - After a universal error handling mode is added, we can change all commands to fail that way by default. - Once we have all commands failing the same way (which would require either the full removal of `try` variants or just making them useless while they're deprecated), `queue_fallible_with_default` could be removed, since its only purpose is to enable commands having different defaults. |
||
![]() |
765166b727
|
Update entity cloning benchmarks (#17084)
# Objective - `entity_cloning` was separated from the rest of the ECS benchmarks. - There was some room for improvement in the benchmarks themselves. - Part of #16647. ## Solution - Merge `entity_cloning` into the rest of the ECS benchmarks. - Apply the `bench!` macro to all benchmark names.\ - Reorganize benchmarks and their helper functions, with more comments than before. - Remove all the extra component definitions (`C2`, `C3`, etc.), and just leave one. Now all entities have exactly one component. ## Testing ```sh # List all entity cloning benchmarks, to verify their names have updated. cargo bench -p benches --bench ecs entity_cloning -- --list # Test benchmarks by running them once. cargo test -p benches --bench ecs entity_cloning # Run all benchmarks (takes about a minute). cargo bench -p benches --bench ecs entity_cloning ``` --- ## Showcase  Interestingly, using `Clone` instead of `Reflect` appears to be 2-2.5 times faster. Furthermore, there were noticeable jumps in time when running the benchmarks:  I theorize this is because the `World` is allocating more space for all the entities, but I don't know for certain. Neat! |
||
![]() |
c890cc9dc3
|
Overhaul picking benchmarks (#17033)
# Objective - Part of #16647. - This PR goes through our `ray_cast::ray_mesh_intersection()` benchmarks and overhauls them with more comments and better extensibility. The code is also a lot less duplicated! ## Solution - Create a `Benchmarks` enum that describes all of the different kind of scenarios we want to benchmark. - Merge all of our existing benchmark functions into a single one, `bench()`, which sets up the scenarios all at once. - Add comments to `mesh_creation()` and `ptoxznorm()`, and move some lines around to be a bit clearer. - Make the benchmarks use the new `bench!` macro, as part of #16647. - Rename many functions and benchmarks to be clearer. ## For reviewers I split this PR up into several, easier to digest commits. You might find it easier to review by looking through each commit, instead of the complete file changes. None of my changes actually modifies the behavior of the benchmarks; they still track the exact same test cases. There shouldn't be significant changes in benchmark performance before and after this PR. ## Testing List all picking benchmarks: `cargo bench -p benches --bench picking -- --list` Run the benchmarks once in debug mode: `cargo test -p benches --bench picking` Run the benchmarks and analyze their performance: `cargo bench -p benches --bench picking` - Check out the generated HTML report in `./target/criterion/report/index.html` once you're done! --- ## Showcase List of all picking benchmarks, after having been renamed: <img width="524" alt="image" src="https://github.com/user-attachments/assets/a1b53daf-4a8b-4c45-a25a-c6306c7175d1" /> Example report for `picking::ray_mesh_intersection::cull_intersect/100_vertices`: <img width="992" alt="image" src="https://github.com/user-attachments/assets/a1aaf53f-ce21-4bef-89c4-b982bb158f5d" /> |
||
![]() |
291cb31798
|
Overhaul bezier curve benchmarks (#17016)
# Objective - Part of #16647. - The benchmarks for bezier curves have several issues and do not yet use the new `bench!` naming scheme. ## Solution - Make all `bevy_math` benchmarks use the `bench!` macro for their name. - Delete the `build_accel_cubic()` benchmark, since it was an exact duplicate of `build_pos_cubic()`. - Remove `collect::<Vec<_>>()` call in `build_pos_cubic()` and replace it with a `for` loop. - Combine all of the benchmarks that measure `curve.position()` under a single group, `curve_position`, and extract the common bench routine into a helper function. - Move the time calculation for the `curve.ease()` benchmark into the setup closure so it is not tracked. - Rename the benchmarks to be more descriptive on what they do. - `easing_1000` -> `segment_ease` - `cubic_position_Vec2` -> `curve_position/vec2` - `cubic_position_Vec3A` -> `curve_position/vec3a` - `cubic_position_Vec3` -> `curve_position/vec3` - `build_pos_cubic_100_points` -> `curve_iter_positions` ## Testing - `cargo test -p benches --bench math` - `cargo bench -p benches --bench math` - Then open `./target/criterion/report/index.html` to see the report! --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
17ad855653
|
Migrate to core::hint::black_box() (#16980)
# Objective Many of our benchmarks use [`criterion::black_box()`](https://docs.rs/criterion/latest/criterion/fn.black_box.html), which is used to prevent the compiler from optimizing away computation that we're trying to time. This can be slow, though, because `criterion::black_box()` forces a point read each time it is called through [`ptr::road_volatile()`](https://doc.rust-lang.org/stable/std/ptr/fn.read_volatile.html). In Rust 1.66, the standard library introduced [`core::hint::black_box()`](https://doc.rust-lang.org/nightly/std/hint/fn.black_box.html) (and `std::hint::black_box()`). This is an intended replacement for `criterion`'s version that uses compiler intrinsics instead of volatile pointer reads, and thus has no runtime overhead. This increases benchmark accuracy, which is always nice 👍 Note that benchmarks may _appear_ to improve in performance after this change, but that's just because we are eliminating the pointer read overhead. ## Solution - Deny `criterion::black_box` in `clippy.toml`. - Fix all imports. ## Testing - `cargo clippy -p benches --benches` |
||
![]() |
f391522483
|
Test benchmarks in CI (#16987)
# Objective - This reverts #16833, and completely goes against #16803. - Turns out running `cargo test --benches` runs each benchmark once, without timing it, just to ensure nothing panics. This is actually desired because we can use it to verify benchmarks are working correctly without all the time constraints of actual benchmarks. ## Solution - Add the `--benches` flag to the CI test command. ## Testing - `cargo run -p ci -- test` |
||
![]() |
c03e494a26
|
Migrate reflection benchmarks to new naming system (#16986)
# Objective - Please see #16647 for the full reasoning behind this change. ## Solution - Create the `bench!` macro, which generates the name of the benchmark at compile time. Migrating is a single line change, and it will automatically update if you move the benchmark to a different module: ```diff + use benches::bench; fn my_benchmark(c: &mut Criterion) { - c.bench_function("my_benchmark", |b| {}); + c.bench_function(bench!("my_benchmark"), |b| {}); } ``` - Migrate all reflection benchmarks to use `bench!`. - Fix a few places where `black_box()` or Criterion is misused. ## Testing ```sh cd benches # Will take a long time! cargo bench --bench reflect # List out the names of all reflection benchmarks, to ensure I didn't miss anything. cargo bench --bench reflect -- --list # Check for linter warnings. cargo clippy --bench reflect # Run each benchmark once. cargo test --bench reflect ``` |
||
![]() |
a7ae0807ac
|
Fix panic in benches caused by missing resources (#16956)
# Objective - To fix the benches panicking on `main` ## Solution - It appears that systems requiring access to a non-existing `Res` now causes a panic - Some of the benches run systems that access resources that have not been inserted into the world - I have made it so that those resources are inserted into the world ## Testing - I ran all the ecs benches and they all run without panicking Co-authored-by: Oliver Maskery <oliver@wellplayed.games> |
||
![]() |
20277006ce
|
Add benchmarks and compile_fail tests back to workspace (#16858)
# Objective - Our benchmarks and `compile_fail` tests lag behind the rest of the engine because they are not in the Cargo workspace, so not checked by CI. - Fixes #16801, please see it for further context! ## Solution - Add benchmarks and `compile_fail` tests to the Cargo workspace. - Fix any leftover formatting issues and documentation. ## Testing - I think CI should catch most things! ## Questions <details> <summary>Outdated issue I was having with function reflection being optional</summary> The `reflection_types` example is failing in Rust-Analyzer for me, but not a normal check. ```rust error[E0004]: non-exhaustive patterns: `ReflectRef::Function(_)` not covered --> examples/reflection/reflection_types.rs:81:11 | 81 | match value.reflect_ref() { | ^^^^^^^^^^^^^^^^^^^ pattern `ReflectRef::Function(_)` not covered | note: `ReflectRef<'_>` defined here --> /Users/bdeep/dev/bevy/bevy/crates/bevy_reflect/src/kind.rs:178:1 | 178 | pub enum ReflectRef<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^ ... 188 | Function(&'a dyn Function), | -------- not covered = note: the matched value is of type `ReflectRef<'_>` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | 126 ~ ReflectRef::Opaque(_) => {}, 127 + ReflectRef::Function(_) => todo!() | ``` I think it is because the following line is feature-gated: |
||
![]() |
20049d4c34
|
Faster entity cloning (#16717)
# Objective #16132 introduced entity cloning functionality, and while it works and is useful, it can be made faster. This is the promised follow-up to improve performance. ## Solution **PREFACE**: This is my first time writing `unsafe` in rust and I have only vague idea about what I'm doing. I would encourage reviewers to scrutinize `unsafe` parts in particular. The solution is to clone component data to an intermediate buffer and use `EntityWorldMut::insert_by_ids` to insert components without additional archetype moves. To facilitate this, `EntityCloner::clone_entity` now reads all components of the source entity and provides clone handlers with the ability to read component data straight from component storage using `read_source_component` and write to an intermediate buffer using `write_target_component`. `ComponentId` is used to check that requested type corresponds to the type available on source entity. Reflect-based handler is a little trickier to pull of: we only have `&dyn Reflect` and no direct access to the underlying data. `ReflectFromPtr` can be used to get `&dyn Reflect` from concrete component data, but to write it we need to create a clone of the underlying data using `Reflect`. For this reason only components that have `ReflectDefault` or `ReflectFromReflect` or `ReflectFromWorld` can be cloned, all other components will be skipped. The good news is that this is actually only a temporary limitation: once #13432 lands we will be able to clone component without requiring one of these `type data`s. This PR also introduces `entity_cloning` benchmark to better compare changes between the PR and main, you can see the results in the **showcase** section. ## Testing - All previous tests passing - Added test for fast reflect clone path (temporary, will be removed after reflection-based cloning lands) - Ran miri ## Showcase Here's a table demonstrating the improvement: | **benchmark** | **main, avg** | **PR, avg** | **change, avg** | | ----------------------- | ------------- | ----------- | --------------- | | many components reflect | 18.505 µs | 2.1351 µs | -89.095% | | hierarchy wide reflect* | 22.778 ms | 4.1875 ms | -81.616% | | hierarchy tall reflect* | 107.24 µs | 26.322 µs | -77.141% | | hierarchy many reflect | 78.533 ms | 9.7415 ms | -87.596% | | many components clone | 1.3633 µs | 758.17 ns | -45.937% | | hierarchy wide clone* | 2.7716 ms | 3.3411 ms | +20.546% | | hierarchy tall clone* | 17.646 µs | 20.190 µs | +17.379% | | hierarchy many clone | 5.8779 ms | 4.2650 ms | -27.439% | *: these benchmarks have entities with only 1 component ## Considerations Once #10154 is resolved a large part of the functionality in this PR will probably become obsolete. It might still be a little bit faster than using command batching, but the complexity might not be worth it. ## Migration Guide - `&EntityCloner` in component clone handlers is changed to `&mut ComponentCloneCtx` to better separate data. - Changed `EntityCloneHandler` from enum to struct and added convenience functions to add default clone and reflect handler more easily. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> |
||
![]() |
6178ce93e8
|
Fix Clippy lints in benchmarks (#16808)
# Objective - Closes #16804. - This copies over our lint configuration to our benchmarks and fixes any lints. ## Solution - Copied over our Clippy configuration from the root `Cargo.toml` to `benches/Cargo.toml`. - Fixed any warnings that Clippy emitted. ## Testing - `cd benches && cargo clippy --benches` |
||
![]() |
c4a24d5b51
|
Make benchmark setup consistent (#16733)
# Objective - Benchmarks are inconsistently setup, there are several that do not compile, and several others that need to be reformatted. - Related / precursor to #16647, this is part of my attempt migrating [`bevy-bencher`](https://github.com/TheBevyFlock/bevy-bencher) to the official benchmarks. ## Solution > [!TIP] > > I recommend reviewing this PR commit-by-commit, instead of all at once! In |
||
![]() |
d21c7a1911
|
bevy_reflect: Function Overloading (Generic & Variadic Functions) (#15074)
# Objective
Currently function reflection requires users to manually monomorphize
their generic functions. For example:
```rust
fn add<T: Add<Output=T>>(a: T, b: T) -> T {
a + b
}
// We have to specify the type of `T`:
let reflect_add = add::<i32>.into_function();
```
This PR doesn't aim to solve that problem—this is just a limitation in
Rust. However, it also means that reflected functions can only ever work
for a single monomorphization. If we wanted to support other types for
`T`, we'd have to create a separate function for each one:
```rust
let reflect_add_i32 = add::<i32>.into_function();
let reflect_add_u32 = add::<u32>.into_function();
let reflect_add_f32 = add::<f32>.into_function();
// ...
```
So in addition to requiring manual monomorphization, we also lose the
benefit of having a single function handle multiple argument types.
If a user wanted to create a small modding script that utilized function
reflection, they'd have to either:
- Store all sets of supported monomorphizations and require users to
call the correct one
- Write out some logic to find the correct function based on the given
arguments
While the first option would work, it wouldn't be very ergonomic. The
second option is better, but it adds additional complexity to the user's
logic—complexity that `bevy_reflect` could instead take on.
## Solution
Introduce [function
overloading](https://en.wikipedia.org/wiki/Function_overloading).
A `DynamicFunction` can now be overloaded with other `DynamicFunction`s.
We can rewrite the above code like so:
```rust
let reflect_add = add::<i32>
.into_function()
.with_overload(add::<u32>)
.with_overload(add::<f32>);
```
When invoked, the `DynamicFunction` will attempt to find a matching
overload for the given set of arguments.
And while I went into this PR only looking to improve generic function
reflection, I accidentally added support for variadic functions as well
(hence why I use the broader term "overload" over "generic").
```rust
// Supports 1 to 4 arguments
let multiply_all = (|a: i32| a)
.into_function()
.with_overload(|a: i32, b: i32| a * b)
.with_overload(|a: i32, b: i32, c: i32| a * b * c)
.with_overload(|a: i32, b: i32, c: i32, d: i32| a * b * c * d);
```
This is simply an added bonus to this particular implementation. ~~Full
variadic support (i.e. allowing for an indefinite number of arguments)
will be added in a later PR.~~ I actually decided to limit the maximum
number of arguments to 63 to supplement faster lookups, a reduced memory
footprint, and faster cloning.
### Alternatives & Rationale
I explored a few options for handling generic functions. This PR is the
one I feel the most confident in, but I feel I should mention the others
and why I ultimately didn't move forward with them.
#### Adding `GenericDynamicFunction`
**TL;DR:** Adding a distinct `GenericDynamicFunction` type unnecessarily
splits and complicates the API.
<details>
<summary>Details</summary>
My initial explorations involved a dedicated `GenericDynamicFunction` to
contain and handle the mappings.
This was initially started back when `DynamicFunction` was distinct from
`DynamicClosure`. My goal was to not prevent us from being able to
somehow make `DynamicFunction` implement `Copy`. But once we reverted
back to a single `DynamicFunction`, that became a non-issue.
But that aside, the real problem was that it created a split in the API.
If I'm using a third-party library that uses function reflection, I have
to know whether to request a `DynamicFunction` or a
`GenericDynamicFunction`. I might not even know ahead of time which one
I want. It might need to be determined at runtime.
And if I'm creating a library, I might want a type to contain both
`DynamicFunction` and `GenericDynamicFunction`. This might not be
possible if, for example, I need to store the function in a `HashMap`.
The other concern is with `IntoFunction`. Right now `DynamicFunction`
trivially implements `IntoFunction` since it can just return itself. But
what should `GenericDynamicFunction` do? It could return itself wrapped
into a `DynamicFunction`, but then the API for `DynamicFunction` would
have to account for this. So then what was the point of having a
separate `GenericDynamicFunction` anyways?
And even apart from `IntoFunction`, there's nothing stopping someone
from manually creating a generic `DynamicFunction` through lying about
its `FunctionInfo` and wrapping a `GenericDynamicFunction`.
That being said, this is probably the "best" alternative if we added a
`Function` trait and stored functions as `Box<dyn Function>`.
However, I'm not convinced we gain much from this. Sure, we could keep
the API for `DynamicFunction` the same, but consumers of `Function` will
need to account for `GenericDynamicFunction` regardless (e.g. handling
multiple `FunctionInfo`, a ranged argument count, etc.). And for all
cases, except where using `DynamicFunction` directly, you end up
treating them all like `GenericDynamicFunction`.
Right now, if we did go with `GenericDynamicFunction`, the only major
benefit we'd gain would be saving 24 bytes. If memory ever does become
an issue here, we could swap over. But I think for the time being it's
better for us to pursue a clearer mental model and end-user ergonomics
through unification.
</details>
##### Using the `FunctionRegistry`
**TL;DR:** Having overloads only exist in the `FunctionRegistry`
unnecessarily splits and complicates the API.
<details>
<summary>Details</summary>
Another idea was to store the overloads in the `FunctionRegistry`. Users
would then just call functions directly through the registry (i.e.
`registry.call("my_func", my_args)`).
I didn't go with this option because of how it specifically relies on
the functions being registered. You'd not only always need access to the
registry, but you'd need to ensure that the functions you want to call
are even registered.
It also means you can't just store a generic `DynamicFunction` on a
type. Instead, you'll need to store the function's name and use that to
look up the function in the registry—even if it's only ever used by that
type.
Doing so also removes all the benefits of `DynamicFunction`, such as the
ability to pass it to functions accepting `IntoFunction`, modify it if
needed, and so on.
Like `GenericDynamicFunction` this introduces a split in the ecosystem:
you either store `DynamicFunction`, store a string to look up the
function, or force `DynamicFunction` to wrap your generic function
anyways. Or worse yet: have `DynamicFunction` wrap the lookup function
using `FunctionRegistryArc`.
</details>
#### Generic `ArgInfo`
**TL;DR:** Allowing `ArgInfo` and `ReturnInfo` to store the generic
information introduces a footgun when interpreting `FunctionInfo`.
<details>
<summary>Details</summary>
Regardless of how we represent a generic function, one thing is clear:
we need to be able to represent the information for such a function.
This PR does so by introducing a `FunctionInfoType` enum to wrap one or
more `FunctionInfo` values.
Originally, I didn't do this. I had `ArgInfo` and `ReturnInfo` allow for
generic types. This allowed us to have a single `FunctionInfo` to
represent our function, but then I realized that it actually lies about
our function.
If we have two `ArgInfo` that both allow for either `i32` or `u32`, what
does this tell us about our function? It turns out: nothing! We can't
know whether our function takes `(i32, i32)`, `(u32, u32)`, `(i32,
u32)`, or `(u32, i32)`.
It therefore makes more sense to just represent a function with multiple
`FunctionInfo` since that's really what it's made up of.
</details>
#### Flatten `FunctionInfo`
**TL;DR:** Flattening removes additional per-overload information some
users may desire and prevents us from adding more information in the
future.
<details>
<summary>Details</summary>
Why don't we just flatten multiple `FunctionInfo` into just one that can
contain multiple signatures?
This is something we could do, but I decided against it for a few
reasons:
- The only thing we'd be able to get rid of for each signature would be
the `name`. While not enough to not do it, it doesn't really suggest we
*have* to either.
- Some consumers may want access to the names of the functions that make
up the overloaded function. For example, to track a bug where an
undesirable function is being added as an overload. Or to more easily
locate the original function of an overload.
- We may eventually allow for more information to be stored on
`FunctionInfo`. For example, we may allow for documentation to be stored
like we do for `TypeInfo`. Consumers of this documentation may want
access to the documentation of each overload as they may provide
documentation specific to that overload.
</details>
## Testing
This PR adds lots of tests and benchmarks, and also adds to the example.
To run the tests:
```
cargo test --package bevy_reflect --all-features
```
To run the benchmarks:
```
cargo bench --bench reflect_function --all-features
```
To run the example:
```
cargo run --package bevy --example function_reflection --all-features
```
### Benchmarks
One of my goals with this PR was to leave the typical case of
non-overloaded functions largely unaffected by the changes introduced in
this PR. ~~And while the static size of `DynamicFunction` has increased
by 17% (from 136 to 160 bytes), the performance has generally stayed the
same~~ The static size of `DynamicFunction` has decreased from 136 to
112 bytes, while calling performance has generally stayed the same:
| | `main` | 7d293ab |
|
||
![]() |
a35811d088
|
Add Immutable Component Support (#16372)
# Objective - Fixes #16208 ## Solution - Added an associated type to `Component`, `Mutability`, which flags whether a component is mutable, or immutable. If `Mutability= Mutable`, the component is mutable. If `Mutability= Immutable`, the component is immutable. - Updated `derive_component` to default to mutable unless an `#[component(immutable)]` attribute is added. - Updated `ReflectComponent` to check if a component is mutable and, if not, panic when attempting to mutate. ## Testing - CI - `immutable_components` example. --- ## Showcase Users can now mark a component as `#[component(immutable)]` to prevent safe mutation of a component while it is attached to an entity: ```rust #[derive(Component)] #[component(immutable)] struct Foo { // ... } ``` This prevents creating an exclusive reference to the component while it is attached to an entity. This is particularly powerful when combined with component hooks, as you can now fully track a component's value, ensuring whatever invariants you desire are upheld. Before this would be done my making a component private, and manually creating a `QueryData` implementation which only permitted read access. <details> <summary>Using immutable components as an index</summary> ```rust /// This is an example of a component like [`Name`](bevy::prelude::Name), but immutable. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Component)] #[component( immutable, on_insert = on_insert_name, on_replace = on_replace_name, )] pub struct Name(pub &'static str); /// This index allows for O(1) lookups of an [`Entity`] by its [`Name`]. #[derive(Resource, Default)] struct NameIndex { name_to_entity: HashMap<Name, Entity>, } impl NameIndex { fn get_entity(&self, name: &'static str) -> Option<Entity> { self.name_to_entity.get(&Name(name)).copied() } } fn on_insert_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) { let Some(&name) = world.entity(entity).get::<Name>() else { unreachable!() }; let Some(mut index) = world.get_resource_mut::<NameIndex>() else { return; }; index.name_to_entity.insert(name, entity); } fn on_replace_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) { let Some(&name) = world.entity(entity).get::<Name>() else { unreachable!() }; let Some(mut index) = world.get_resource_mut::<NameIndex>() else { return; }; index.name_to_entity.remove(&name); } // Setup our name index world.init_resource::<NameIndex>(); // Spawn some entities! let alyssa = world.spawn(Name("Alyssa")).id(); let javier = world.spawn(Name("Javier")).id(); // Check our index let index = world.resource::<NameIndex>(); assert_eq!(index.get_entity("Alyssa"), Some(alyssa)); assert_eq!(index.get_entity("Javier"), Some(javier)); // Changing the name of an entity is also fully capture by our index world.entity_mut(javier).insert(Name("Steven")); // Javier changed their name to Steven let steven = javier; // Check our index let index = world.resource::<NameIndex>(); assert_eq!(index.get_entity("Javier"), None); assert_eq!(index.get_entity("Steven"), Some(steven)); ``` </details> Additionally, users can use `Component<Mutability = ...>` in trait bounds to enforce that a component _is_ mutable or _is_ immutable. When using `Component` as a trait bound without specifying `Mutability`, any component is applicable. However, methods which only work on mutable or immutable components are unavailable, since the compiler must be pessimistic about the type. ## Migration Guide - When implementing `Component` manually, you must now provide a type for `Mutability`. The type `Mutable` provides equivalent behaviour to earlier versions of `Component`: ```rust impl Component for Foo { type Mutability = Mutable; // ... } ``` - When working with generic components, you may need to specify that your generic parameter implements `Component<Mutability = Mutable>` rather than `Component` if you require mutable access to said component. - The entity entry API has had to have some changes made to minimise friction when working with immutable components. Methods which previously returned a `Mut<T>` will now typically return an `OccupiedEntry<T>` instead, requiring you to add an `into_mut()` to get the `Mut<T>` item again. ## Draft Release Notes Components can now be made immutable while stored within the ECS. Components are the fundamental unit of data within an ECS, and Bevy provides a number of ways to work with them that align with Rust's rules around ownership and borrowing. One part of this is hooks, which allow for defining custom behavior at key points in a component's lifecycle, such as addition and removal. However, there is currently no way to respond to _mutation_ of a component using hooks. The reasons for this are quite technical, but to summarize, their addition poses a significant challenge to Bevy's core promises around performance. Without mutation hooks, it's relatively trivial to modify a component in such a way that breaks invariants it intends to uphold. For example, you can use `core::mem::swap` to swap the components of two entities, bypassing the insertion and removal hooks. This means the only way to react to this modification is via change detection in a system, which then begs the question of what happens _between_ that alteration and the next run of that system? Alternatively, you could make your component private to prevent mutation, but now you need to provide commands and a custom `QueryData` implementation to allow users to interact with your component at all. Immutable components solve this problem by preventing the creation of an exclusive reference to the component entirely. Without an exclusive reference, the only way to modify an immutable component is via removal or replacement, which is fully captured by component hooks. To make a component immutable, simply add `#[component(immutable)]`: ```rust #[derive(Component)] #[component(immutable)] struct Foo { // ... } ``` When implementing `Component` manually, there is an associated type `Mutability` which controls this behavior: ```rust impl Component for Foo { type Mutability = Mutable; // ... } ``` Note that this means when working with generic components, you may need to specify that a component is mutable to gain access to certain methods: ```rust // Before fn bar<C: Component>() { // ... } // After fn bar<C: Component<Mutability = Mutable>>() { // ... } ``` With this new tool, creating index components, or caching data on an entity should be more user friendly, allowing libraries to provide APIs relying on components and hooks to uphold their invariants. ## Notes - ~~I've done my best to implement this feature, but I'm not happy with how reflection has turned out. If any reflection SMEs know a way to improve this situation I'd greatly appreciate it.~~ There is an outstanding issue around the fallibility of mutable methods on `ReflectComponent`, but the DX is largely unchanged from `main` now. - I've attempted to prevent all safe mutable access to a component that does not implement `Component<Mutability = Mutable>`, but there may still be some methods I have missed. Please indicate so and I will address them, as they are bugs. - Unsafe is an escape hatch I am _not_ attempting to prevent. Whatever you do with unsafe is between you and your compiler. - I am marking this PR as ready, but I suspect it will undergo fairly major revisions based on SME feedback. - I've marked this PR as _Uncontroversial_ based on the feature, not the implementation. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Co-authored-by: Nuutti Kotivuori <naked@iki.fi> |
||
![]() |
b1e4512648
|
Fix the picking backend features not actually disabling the features (#16470)
# Objective - Fixes #16469. ## Solution - Make the picking backend features not enabled by default in each sub-crate. - Make features in `bevy_internal` to set the backend features - Make the root `bevy` crate set the features by default. ## Testing - The mesh and sprite picking examples still work correctly. |
||
![]() |
3ada15ee1c
|
Add more Glam types and constructors to prelude (#16261)
# Objective Glam has some common and useful types and helpers that are not in the prelude of `bevy_math`. This includes shorthand constructors like `vec3`, or even `Vec3A`, the aligned version of `Vec3`. ```rust // The "normal" way to create a 3D vector let vec = Vec3::new(2.0, 1.0, -3.0); // Shorthand version let vec = vec3(2.0, 1.0, -3.0); ``` ## Solution Add the following types and methods to the prelude: - `vec2`, `vec3`, `vec3a`, `vec4` - `uvec2`, `uvec3`, `uvec4` - `ivec2`, `ivec3`, `ivec4` - `bvec2`, `bvec3`, `bvec3a`, `bvec4`, `bvec4a` - `mat2`, `mat3`, `mat3a`, `mat4` - `quat` (not sure if anyone uses this, but for consistency) - `Vec3A` - `BVec3A`, `BVec4A` - `Mat3A` I did not add the u16, i16, or f64 variants like `dvec2`, since there are currently no existing types like those in the prelude. The shorthand constructors are currently used a lot in some places in Bevy, and not at all in others. In a follow-up, we might want to consider if we have a preference for the shorthand, and make a PR to change the codebase to use it more consistently. |
||
![]() |
4df8b1998e
|
Allow or fix dead code in benches (#16282)
# Objective Fixes #15806 ## Solution Fix an undeclared module and expect `dead_code`. ## Testing Run this command and see no `dead_code` warnings. `cargo +nightly check --benches --target-dir ../target --manifest-path ./benches/Cargo.toml` |
||
![]() |
54b323ec80
|
Mesh picking fixes (#16110)
# Objective - Mesh picking is noisy when a non triangle list is used - Mesh picking runs even when users don't need it - Resolve #16065 ## Solution - Don't add the mesh picking plugin by default - Remove error spam |
||
![]() |
e5e44888c6
|
Validate param benchmarks (#15885)
# Objective Benchmark overhead of validation for: - `DynSystemParam`, - `ParamSet`, - combinator systems. Needed for #15606 ## Solution As noted in objective, I've added 3 benchmarks, where each uses an excessive amount of the specific functionality. I benchmark on the level of schedules, rather than individual `validate_param` calls, so we get a better idea how changes to the code impact memory-lookup, etc. related side effects. ## Testing ``` param/combinator_system/8_piped_systems time: [1.7560 µs 1.7865 µs 1.8180 µs] change: [+4.5244% +6.7955% +9.1413%] (p = 0.00 < 0.05) Performance has regressed. Found 2 outliers among 100 measurements (2.00%) 1 (1.00%) high mild 1 (1.00%) high severe param/combinator_system/8_dyn_params_system time: [89.354 ns 89.790 ns 90.300 ns] change: [+0.6751% +1.6825% +2.6842%] (p = 0.00 < 0.05) Change within noise threshold. Found 9 outliers among 100 measurements (9.00%) 6 (6.00%) high mild 3 (3.00%) high severe param/combinator_system/8_variant_param_set_system time: [88.295 ns 89.202 ns 90.208 ns] change: [+0.1320% +1.0060% +1.8482%] (p = 0.02 < 0.05) Change within noise threshold. Found 4 outliers among 100 measurements (4.00%) 4 (4.00%) high mild ``` 2 back-to-back runs of the benchmarks, there is quire a lot of noise, can use feedback on fixing that |
||
![]() |
d96a9d15f6
|
Migrate from Query::single and friends to Single (#15872)
# Objective - closes #15866 ## Solution - Simply migrate where possible. ## Testing - Expect that CI will do most of the work. Examples is another way of testing this, as most of the work is in that area. --- ## Notes For now, this PR doesn't migrate `QueryState::single` and friends as for now, this look like another issue. So for example, QueryBuilders that used single or `World::query` that used single wasn't migrated. If there is a easy way to migrate those, please let me know. Most of the uses of `Query::single` were removed, the only other uses that I found was related to tests of said methods, so will probably be removed when we remove `Query::single`. |
||
![]() |
3d6b24880e
|
Add insert_batch and variations (#15702)
# Objective `insert_or_spawn_batch` exists, but a version for just inserting doesn't - Closes #2693 - Closes #8384 - Adopts/supersedes #8600 ## Solution Add `insert_batch`, along with the most common `insert` variations: - `World::insert_batch` - `World::insert_batch_if_new` - `World::try_insert_batch` - `World::try_insert_batch_if_new` - `Commands::insert_batch` - `Commands::insert_batch_if_new` - `Commands::try_insert_batch` - `Commands::try_insert_batch_if_new` ## Testing Added tests, and added a benchmark for `insert_batch`. Performance is slightly better than `insert_or_spawn_batch` when only inserting:  <details> <summary>old benchmark</summary> This was before reworking it to remove the `UnsafeWorldCell`:  </details> --- ## Showcase Usage is the same as `insert_or_spawn_batch`: ``` use bevy_ecs::{entity::Entity, world::World, component::Component}; #[derive(Component)] struct A(&'static str); #[derive(Component, PartialEq, Debug)] struct B(f32); let mut world = World::new(); let entity_a = world.spawn_empty().id(); let entity_b = world.spawn_empty().id(); world.insert_batch([ (entity_a, (A("a"), B(0.0))), (entity_b, (A("b"), B(1.0))), ]); assert_eq!(world.get::<B>(entity_a), Some(&B(0.0))); ``` |
||
![]() |
0e30b68b20
|
Add mesh picking backend and MeshRayCast system parameter (#15800)
# Objective Closes #15545. `bevy_picking` supports UI and sprite picking, but not mesh picking. Being able to pick meshes would be extremely useful for various games, tools, and our own examples, as well as scene editors and inspectors. So, we need a mesh picking backend! Luckily, [`bevy_mod_picking`](https://github.com/aevyrie/bevy_mod_picking) (which `bevy_picking` is based on) by @aevyrie already has a [backend for it]( |
||
![]() |
219b5930f1
|
Rename App/World::observe to add_observer , EntityWorldMut::observe_entity to observe . (#15754)
# Objective - Closes #15752 Calling the functions `App::observe` and `World::observe` doesn't make sense because you're not "observing" the `App` or `World`, you're adding an observer that listens for an event that occurs *within* the `World`. We should rename them to better fit this. ## Solution Renames: - `App::observe` -> `App::add_observer` - `World::observe` -> `World::add_observer` - `Commands::observe` -> `Commands::add_observer` - `EntityWorldMut::observe_entity` -> `EntityWorldMut::observe` (Note this isn't a breaking change as the original rename was introduced earlier this cycle.) ## Testing Reusing current tests. |
||
![]() |
d1bd46d45e
|
Deprecate get_or_spawn (#15652)
# Objective After merging retained rendering world #15320, we now have a good way of creating a link between worlds (*HIYAA intensifies*). This means that `get_or_spawn` is no longer necessary for that function. Entity should be opaque as the warning above `get_or_spawn` says. This is also part of #15459. I'm deprecating `get_or_spawn_batch` in a different PR in order to keep the PR small in size. ## Solution Deprecate `get_or_spawn` and replace it with `get_entity` in most contexts. If it's possible to query `&RenderEntity`, then the entity is synced and `render_entity.id()` is initialized in the render world. ## Migration Guide If you are given an `Entity` and you want to do something with it, use `Commands.entity(...)` or `World.entity(...)`. If instead you want to spawn something use `Commands.spawn(...)` or `World.spawn(...)`. If you are not sure if an entity exists, you can always use `get_entity` and match on the `Option<...>` that is returned. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |