ce7d4e41d6
28 Commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
![]() |
ce7d4e41d6
|
Make system param validation rely on the unified ECS error handling via the GLOBAL_ERROR_HANDLER (#18454)
# Objective There are two related problems here: 1. Users should be able to change the fallback behavior of *all* ECS-based errors in their application by setting the `GLOBAL_ERROR_HANDLER`. See #18351 for earlier work in this vein. 2. The existing solution (#15500) for customizing this behavior is high on boilerplate, not global and adds a great deal of complexity. The consensus is that the default behavior when a parameter fails validation should be set based on the kind of system parameter in question: `Single` / `Populated` should silently skip the system, but `Res` should panic. Setting this behavior at the system level is a bandaid that makes getting to that ideal behavior more painful, and can mask real failures (if a resource is missing but you've ignored a system to make the Single stop panicking you're going to have a bad day). ## Solution I've removed the existing `ParamWarnPolicy`-based configuration, and wired up the `GLOBAL_ERROR_HANDLER`/`default_error_handler` to the various schedule executors to properly plumb through errors . Additionally, I've done a small cleanup pass on the corresponding example. ## Testing I've run the `fallible_params` example, with both the default and a custom global error handler. The former panics (as expected), and the latter spams the error console with warnings 🥲 ## Questions for reviewers 1. Currently, failed system param validation will result in endless console spam. Do you want me to implement a solution for warn_once-style debouncing somehow? 2. Currently, the error reporting for failed system param validation is very limited: all we get is that a system param failed validation and the name of the system. Do you want me to implement improved error reporting by bubbling up errors in this PR? 3. There is broad consensus that the default behavior for failed system param validation should be set on a per-system param basis. Would you like me to implement that in this PR? My gut instinct is that we absolutely want to solve 2 and 3, but it will be much easier to do that work (and review it) if we split the PRs apart. ## Migration Guide `ParamWarnPolicy` and the `WithParamWarnPolicy` have been removed completely. Failures during system param validation are now handled via the `GLOBAL_ERROR_HANDLER`: please see the `bevy_ecs::error` module docs for more information. --------- Co-authored-by: MiniaczQ <xnetroidpl@gmail.com> |
||
![]() |
5d0505a85e
|
Unify and simplify command and system error handling (#18351)
# Objective - ECS error handling is a lovely flagship feature for Bevy 0.16, all in the name of reducing panics and encouraging better error handling (#14275). - Currently though, command and system error handling are completely disjoint and use different mechanisms. - Additionally, there's a number of distinct ways to set the default/fallback/global error handler that have limited value. As far as I can tell, this will be cfg flagged to toggle between dev and production builds in 99.9% of cases, with no real value in more granular settings or helpers. - Fixes #17272 ## Solution - Standardize error handling on the OnceLock global error mechanisms ironed out in https://github.com/bevyengine/bevy/pull/17215 - As discussed there, there are serious performance concerns there, especially for commands - I also think this is a better fit for the use cases, as it's truly global - Move from `SystemErrorContext` to a more general purpose `ErrorContext`, which can handle observers and commands more clearly - Cut the superfluous setter methods on `App` and `SubApp` - Rename the limited (and unhelpful) `fallible_systems` example to `error_handling`, and add an example of command error handling ## Testing Ran the `error_handling` example. ## Notes for reviewers - Do you see a clear way to allow commands to retain &mut World access in the per-command custom error handlers? IMO that's a key feature here (allowing the ad-hoc creation of custom commands), but I'm not sure how to get there without exploding complexity. - I've removed the feature gate on the default_error_handler: contrary to @cart's opinion in #17215 I think that virtually all apps will want to use this. Can you think of a category of app that a) is extremely performance sensitive b) is fine with shipping to production with the panic error handler? If so, I can try to gather performance numbers and/or reintroduce the feature flag. UPDATE: see benches at the end of this message. - ~~`OnceLock` is in `std`: @bushrat011899 what should we do here?~~ - Do you have ideas for more automated tests for this collection of features? ## Benchmarks I checked the impact of the feature flag introduced: benchmarks might show regressions. This bears more investigation. I'm still skeptical that there are users who are well-served by a fast always panicking approach, but I'm going to re-add the feature flag here to avoid stalling this out.  --------- Co-authored-by: Zachary Harrold <zac@harrold.com.au> |
||
![]() |
8570af1d96
|
Add print_stdout and print_stderr lints (#17446) (#18233)
# Objective - Prevent usage of `println!`, `eprintln!` and the like because they require `std` - Fixes #17446 ## Solution - Enable the `print_stdout` and `print_stderr` clippy lints - Replace all `println!` and `eprintln!` occurrences with `log::*` where applicable or alternatively ignore the warnings ## Testing - Run `cargo clippy --workspace` to ensure that there are no warnings relating to printing to `stdout` or `stderr` |
||
![]() |
cca5813472
|
BevyError: Bevy's new catch-all error type (#18144)
## Objective Fixes #18092 Bevy's current error type is a simple type alias for `Box<dyn Error + Send + Sync + 'static>`. This largely works as a catch-all error, but it is missing a critical feature: the ability to capture a backtrace at the point that the error occurs. The best way to do this is `anyhow`-style error handling: a new error type that takes advantage of the fact that the `?` `From` conversion happens "inline" to capture the backtrace at the point of the error. ## Solution This PR adds a new `BevyError` type (replacing our old `std::error::Error` type alias), which uses the "from conversion backtrace capture" approach: ```rust fn oh_no() -> Result<(), BevyError> { // this fails with Rust's built in ParseIntError, which // is converted into the catch-all BevyError type let number: usize = "hi".parse()?; println!("parsed {number}"); Ok(()) } ``` This also updates our exported `Result` type alias to default to `BevyError`, meaning you can write this instead: ```rust fn oh_no() -> Result { let number: usize = "hi".parse()?; println!("parsed {number}"); Ok(()) } ``` When a BevyError is encountered in a system, it will use Bevy's default system error handler (which panics by default). BevyError does custom "backtrace filtering" by default, meaning we can cut out the _massive_ amount of "rust internals", "async executor internals", and "bevy system scheduler internals" that show up in backtraces. It also trims out the first generally-unnecssary `From` conversion backtrace lines that make it harder to locate the real error location. The result is a blissfully simple backtrace by default:  The full backtrace can be shown by setting the `BEVY_BACKTRACE=full` environment variable. Non-BevyError panics still use the default Rust backtrace behavior. One issue that prevented the truly noise-free backtrace during panics that you see above is that Rust's default panic handler will print the unfiltered (and largely unhelpful real-panic-point) backtrace by default, in _addition_ to our filtered BevyError backtrace (with the helpful backtrace origin) that we capture and print. To resolve this, I have extended Bevy's existing PanicHandlerPlugin to wrap the default panic handler. If we panic from the result of a BevyError, we will skip the default "print full backtrace" panic handler. This behavior can be enabled and disabled using the new `error_panic_hook` cargo feature in `bevy_app` (which is enabled by default). One downside to _not_ using `Box<dyn Error>` directly is that we can no longer take advantage of the built-in `Into` impl for strings to errors. To resolve this, I have added the following: ```rust // Before Err("some error")? // After Err(BevyError::message("some error"))? ``` We can discuss adding shorthand methods or macros for this (similar to anyhow's `anyhow!("some error")` macro), but I'd prefer to discuss that later. I have also added the following extension method: ```rust // Before some_option.ok_or("some error")?; // After some_option.ok_or_message("some error")?; ``` I've also moved all of our existing error infrastructure from `bevy_ecs::result` to `bevy_ecs::error`, as I think that is the better home for it ## Why not anyhow (or eyre)? The biggest reason is that `anyhow` needs to be a "generically useful error type", whereas Bevy is a much narrower scope. By using our own error, we can be significantly more opinionated. For example, anyhow doesn't do the extensive (and invasive) backtrace filtering that BevyError does because it can't operate on Bevy-specific context, and needs to be generically useful. Bevy also has a lot of operational context (ex: system info) that could be useful to attach to errors. If we have control over the error type, we can add whatever context we want to in a structured way. This could be increasingly useful as we add more visual / interactive error handling tools and editor integrations. Additionally, the core approach used is simple and requires almost no code. anyhow clocks in at ~2500 lines of code, but the impl here uses 160. We are able to boil this down to exactly what we need, and by doing so we improve our compile times and the understandability of our code. |
||
![]() |
fd67ca7eb0
|
feat(ecs): configurable error handling for fallible systems (#17753)
You can now configure error handlers for fallible systems. These can be configured on several levels: - Globally via `App::set_systems_error_handler` - Per-schedule via `Schedule::set_error_handler` - Per-system via a piped system (this is existing functionality) The default handler of panicking on error keeps the same behavior as before this commit. The "fallible_systems" example demonstrates the new functionality. This builds on top of #17731, #16589, #17051. --------- Signed-off-by: Jean Mertz <git@jeanmertz.com> |
||
![]() |
17c46f4add
|
bevy_ecs: Apply #![warn(clippy::allow_attributes, clippy::allow_attributes_without_reason)] (#17335)
# Objective - https://github.com/bevyengine/bevy/issues/17111 ## Solution Set the `clippy::allow_attributes` and `clippy::allow_attributes_without_reason` lints to `warn`, and bring `bevy_ecs` in line with the new restrictions. ## Testing This PR is a WIP; testing will happen after it's finished. |
||
![]() |
0403948aa2
|
Remove Implicit std Prelude from no_std Crates (#17086)
# Background In `no_std` compatible crates, there is often an `std` feature which will allow access to the standard library. Currently, with the `std` feature _enabled_, the [`std::prelude`](https://doc.rust-lang.org/std/prelude/index.html) is implicitly imported in all modules. With the feature _disabled_, instead the [`core::prelude`](https://doc.rust-lang.org/core/prelude/index.html) is implicitly imported. This creates a subtle and pervasive issue where `alloc` items _may_ be implicitly included (if `std` is enabled), or must be explicitly included (if `std` is not enabled). # Objective - Make the implicit imports for `no_std` crates consistent regardless of what features are/not enabled. ## Solution - Replace the `cfg_attr` "double negative" `no_std` attribute with conditional compilation to _include_ `std` as an external crate. ```rust // Before #![cfg_attr(not(feature = "std"), no_std)] // After #![no_std] #[cfg(feature = "std")] extern crate std; ``` - Fix imports that are currently broken but are only now visible with the above fix. ## Testing - CI ## Notes I had previously used the "double negative" version of `no_std` based on general consensus that it was "cleaner" within the Rust embedded community. However, this implicit prelude issue likely was considered when forming this consensus. I believe the reason why is the items most affected by this issue are provided by the `alloc` crate, which is rarely used within embedded but extensively used within Bevy. |
||
![]() |
ac43d5c94f
|
Convert to fallible system in IntoSystemConfigs (#17051)
# Objective - #16589 added an enum to switch between fallible and infallible system. This branching should be unnecessary if we wrap infallible systems in a function to return `Ok(())`. ## Solution - Create a wrapper system for `System<(), ()>`s that returns `Ok` on the call to `run` and `run_unsafe`. The wrapper should compile out, but I haven't checked. - I removed the `impl IntoSystemConfigs for BoxedSystem<(), ()>` as I couldn't figure out a way to keep the impl without double boxing. ## Testing - ran `many_foxes` example to check if it still runs. ## Migration Guide - `IntoSystemConfigs` has been removed for `BoxedSystem<(), ()>`. Either use `InfallibleSystemWrapper` before boxing or make your system return `bevy::ecs::prelude::Result`. |
||
![]() |
394e82f4bc
|
panic on system error (#16979)
# Objective - First step for #16718 - #16589 introduced an api that can only ignore errors, which is risky ## Solution - Panic instead of just ignoring the errors ## Testing - Changed the `fallible_systems` example to return an error ``` Encountered an error in system `fallible_systems::setup`: TooManyVertices { subdivisions: 300, number_of_resulting_points: 906012 } Encountered a panic in system `fallible_systems::setup`! Encountered a panic in system `bevy_app::main_schedule::Main::run_main`! ``` |
||
![]() |
1f2d0e6308
|
Add no_std support to bevy_ecs (#16758)
# Objective - Contributes to #15460 ## Solution - Added the following features: - `std` (default) - `async_executor` (default) - `edge_executor` - `critical-section` - `portable-atomic` - Gated `tracing` in `bevy_utils` to allow compilation on certain platforms - Switched from `tracing` to `log` for simple message logging within `bevy_ecs`. Note that `tracing` supports capturing from `log` so this should be an uncontroversial change. - Fixed imports and added feature gates as required - Made `bevy_tasks` optional within `bevy_ecs`. Turns out it's only needed for parallel operations which are already gated behind `multi_threaded` anyway. ## Testing - Added to `compile-check-no-std` CI command - `cargo check -p bevy_ecs --no-default-features --features edge_executor,critical-section,portable-atomic --target thumbv6m-none-eabi` - `cargo check -p bevy_ecs --no-default-features --features edge_executor,critical-section` - `cargo check -p bevy_ecs --no-default-features` ## Draft Release Notes Bevy's core ECS now supports `no_std` platforms. In prior versions of Bevy, it was not possible to work with embedded or niche platforms due to our reliance on the standard library, `std`. This has blocked a number of novel use-cases for Bevy, such as an embedded database for IoT devices, or for creating games on retro consoles. With this release, `bevy_ecs` no longer requires `std`. To use Bevy on a `no_std` platform, you must disable default features and enable the new `edge_executor` and `critical-section` features. You may also need to enable `portable-atomic` and `critical-section` if your platform does not natively support all atomic types and operations used by Bevy. ```toml [dependencies] bevy_ecs = { version = "0.16", default-features = false, features = [ # Required for platforms with incomplete atomics (e.g., Raspberry Pi Pico) "portable-atomic", "critical-section", # Optional "bevy_reflect", "serialize", "bevy_debug_stepping", "edge_executor" ] } ``` Currently, this has been tested on bare-metal x86 and the Raspberry Pi Pico. If you have trouble using `bevy_ecs` on a particular platform, please reach out either through a GitHub issue or in the `no_std` working group on the Bevy Discord server. Keep an eye out for future `no_std` updates as we continue to improve the parity between `std` and `no_std`. We look forward to seeing what kinds of applications are now possible with Bevy! ## Notes - Creating PR in draft to ensure CI is passing before requesting reviews. - This implementation has no support for multithreading in `no_std`, especially due to `NonSend` being unsound if allowed in multithreading. The reason is we cannot check the `ThreadId` in `no_std`, so we have no mechanism to at-runtime determine if access is sound. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Vic <59878206+Victoronz@users.noreply.github.com> |
||
![]() |
0070514f54
|
Fallible systems (#16589)
# Objective Error handling in bevy is hard. See for reference https://github.com/bevyengine/bevy/issues/11562, https://github.com/bevyengine/bevy/issues/10874 and https://github.com/bevyengine/bevy/issues/12660. The goal of this PR is to make it better, by allowing users to optionally return `Result` from systems as outlined by Cart in <https://github.com/bevyengine/bevy/issues/14275#issuecomment-2223708314>. ## Solution This PR introduces a new `ScheuleSystem` type to represent systems that can be added to schedules. Instances of this type contain either an infallible `BoxedSystem<(), ()>` or a fallible `BoxedSystem<(), Result>`. `ScheuleSystem` implements `System<In = (), Out = Result>` and replaces all uses of `BoxedSystem` in schedules. The async executor now receives a result after executing a system, which for infallible systems is always `Ok(())`. Currently it ignores this result, but more useful error handling could also be implemented. Aliases for `Error` and `Result` have been added to the `bevy_ecs` prelude, as well as const `OK` which new users may find more friendly than `Ok(())`. ## Testing - Currently there are not actual semantics changes that really require new tests, but I added a basic one just to make sure we don't break stuff in the future. - The behavior of existing systems is totally unchanged, including logging. - All of the existing systems tests pass, and I have not noticed anything strange while playing with the examples ## Showcase The following minimal example prints "hello world" once, then completes. ```rust use bevy::prelude::*; fn main() { App::new().add_systems(Update, hello_world_system).run(); } fn hello_world_system() -> Result { println!("hello world"); Err("string")?; println!("goodbye world"); OK } ``` ## Migration Guide This change should be pretty much non-breaking, except for users who have implemented their own custom executors. Those users should use `ScheduleSystem` in place of `BoxedSystem<(), ()>` and import the `System` trait where needed. They can choose to do whatever they wish with the result. ## Current Work + [x] Fix tests & doc comments + [x] Write more tests + [x] Add examples + [X] Draft release notes ## Draft Release Notes As of this release, systems can now return results. First a bit of background: Bevy has hisotrically expected systems to return the empty type `()`. While this makes sense in the context of the ecs, it's at odds with how error handling is typically done in rust: returning `Result::Error` to indicate failure, and using the short-circuiting `?` operator to propagate that error up the call stack to where it can be properly handled. Users of functional languages will tell you this is called "monadic error handling". Not being able to return `Results` from systems left bevy users with a quandry. They could add custom error handling logic to every system, or manually pipe every system into an error handler, or perhaps sidestep the issue with some combination of fallible assignents, logging, macros, and early returns. Often, users would just litter their systems with unwraps and possible panics. While any one of these approaches might be fine for a particular user, each of them has their own drawbacks, and none makes good use of the language. Serious issues could also arrise when two different crates used by the same project made different choices about error handling. Now, by returning results, systems can defer error handling to the application itself. It looks like this: ```rust // Previous, handling internally app.add_systems(my_system) fn my_system(window: Query<&Window>) { let Ok(window) = query.get_single() else { return; }; // ... do something to the window here } // Previous, handling externally app.add_systems(my_system.pipe(my_error_handler)) fn my_system(window: Query<&Window>) -> Result<(), impl Error> { let window = query.get_single()?; // ... do something to the window here Ok(()) } // Previous, panicking app.add_systems(my_system) fn my_system(window: Query<&Window>) { let window = query.single(); // ... do something to the window here } // Now app.add_systems(my_system) fn my_system(window: Query<&Window>) -> Result { let window = query.get_single()?; // ... do something to the window here Ok(()) } ``` There are currently some limitations. Systems must either return `()` or `Result<(), Box<dyn Error + Send + Sync + 'static>>`, with no in-between. Results are also ignored by default, and though implementing a custom handler is possible, it involves writing your own custom ecs executor (which is *not* recomended). Systems should return errors when they cannot perform their normal behavior. In turn, errors returned to the executor while running the schedule will (eventually) be treated as unexpected. Users and library authors should prefer to return errors for anything that disrupts the normal expected behavior of a system, and should only handle expected cases internally. We have big plans for improving error handling further: + Allowing users to change the error handling logic of the default executors. + Adding source tracking and optional backtraces to errors. + Possibly adding tracing-levels (Error/Warn/Info/Debug/Trace) to errors. + Generally making the default error logging more helpful and inteligent. + Adding monadic system combininators for fallible systems. + Possibly removing all panicking variants from our api. --------- Co-authored-by: Zachary Harrold <zac@harrold.com.au> |
||
![]() |
f87b9fe20c
|
Turn apply_deferred into a ZST System (#16642)
# Objective - Required by #16622 due to differing implementations of `System` by `FunctionSystem` and `ExclusiveFunctionSystem`. - Optimize the memory usage of instances of `apply_deferred` in system schedules. ## Solution By changing `apply_deferred` from being an ordinary system that ends up as an `ExclusiveFunctionSystem`, and instead into a ZST struct that implements `System` manually, we save ~320 bytes per instance of `apply_deferred` in any schedule. ## Testing - All current tests pass. --- ## Migration Guide - If you were previously calling the special `apply_deferred` system via `apply_deferred(world)`, don't. |
||
![]() |
acea4e7e6f
|
Better warnings about invalid parameters (#15500)
# Objective System param validation warnings should be configurable and default to "warn once" (per system). Fixes: #15391 ## Solution `SystemMeta` is given a new `ParamWarnPolicy` field. The policy decides whether warnings will be emitted by each system param when it fails validation. The policy is updated by the system after param validation fails. Example warning: ``` 2024-09-30T18:10:04.740749Z WARN bevy_ecs::system::function_system: System fallible_params::do_nothing_fail_validation will not run because it requested inaccessible system parameter Single<(), (With<Player>, With<Enemy>)> ``` Currently, only the first invalid parameter is displayed. Warnings can be disabled on function systems using `.param_never_warn()`. (there is also `.with_param_warn_policy(policy)`) ## Testing Ran `fallible_params` example. --------- Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net> |
||
![]() |
d70595b667
|
Add core and alloc over std Lints (#15281)
# Objective - Fixes #6370 - Closes #6581 ## Solution - Added the following lints to the workspace: - `std_instead_of_core` - `std_instead_of_alloc` - `alloc_instead_of_core` - Used `cargo +nightly fmt` with [item level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Item%5C%3A) to split all `use` statements into single items. - Used `cargo clippy --workspace --all-targets --all-features --fix --allow-dirty` to _attempt_ to resolve the new linting issues, and intervened where the lint was unable to resolve the issue automatically (usually due to needing an `extern crate alloc;` statement in a crate root). - Manually removed certain uses of `std` where negative feature gating prevented `--all-features` from finding the offending uses. - Used `cargo +nightly fmt` with [crate level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Crate%5C%3A) to re-merge all `use` statements matching Bevy's previous styling. - Manually fixed cases where the `fmt` tool could not re-merge `use` statements due to conditional compilation attributes. ## Testing - Ran CI locally ## Migration Guide The MSRV is now 1.81. Please update to this version or higher. ## Notes - This is a _massive_ change to try and push through, which is why I've outlined the semi-automatic steps I used to create this PR, in case this fails and someone else tries again in the future. - Making this change has no impact on user code, but does mean Bevy contributors will be warned to use `core` and `alloc` instead of `std` where possible. - This lint is a critical first step towards investigating `no_std` options for Bevy. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
![]() |
740d1cc9ff
|
Fix system param warnings on systems that cannot run anyways (#15397)
# Objective Fix "system skipped" warnings when validation fails on systems that wouldn't run because of run conditions. ## Solution > I think the error is from a system defined as: > > ```rust > no_gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD> > .run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>), > ``` > > So the `run_if` was preventing the panics. Maybe we need to skip validation if `!system_conditions_met`, or at least silence the warning in that case. *By @chescock in https://discord.com/channels/691052431525675048/692572690833473578/1287865365312831562* Validation of system is skipped if the system was already skipped by run conditions. ## Testing Ran alien addict example, no more warnings. |
||
![]() |
e312da8c52
|
Reduce runtime panics through SystemParam validation (#15276)
# Objective The goal of this PR is to introduce `SystemParam` validation in order to reduce runtime panics. Fixes #15265 ## Solution `SystemParam` now has a new method `validate_param(...) -> bool`, which takes immutable variants of `get_param` arguments. The returned value indicates whether the parameter can be acquired from the world. If parameters cannot be acquired for a system, it won't be executed, similarly to run conditions. This reduces panics when using params like `Res`, `ResMut`, etc. as well as allows for new, ergonomic params like #15264 or #15302. Param validation happens at the level of executors. All validation happens directly before executing a system, in case of normal systems they are skipped, in case of conditions they return false. Warning about system skipping is primitive and subject to change in subsequent PRs. ## Testing Two executor tests check that all executors: - skip systems which have invalid parameters: - piped systems get skipped together, - dependent systems still run correctly, - skip systems with invalid run conditions: - system conditions have invalid parameters, - system set conditions have invalid parameters. |
||
![]() |
aea9b4a9e4
|
Simplified backtraces (#12305)
# Objective Remove Bevy internals from backtraces ## Solution Executors insert `__rust_begin_short_backtrace` into the callstack before running a system. <details> <summary>Example current output</summary> ``` thread 'Compute Task Pool (3)' panicked at src/main.rs:7:33: Foo stack backtrace: 0: rust_begin_unwind at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:647:5 1: core::panicking::panic_fmt at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panicking.rs:72:14 2: foo::main::{{closure}} at ./src/main.rs:7:33 3: core::ops::function::impls::<impl core::ops::function::FnMut<A> for &mut F>::call_mut at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/ops/function.rs:294:13 4: <Func as bevy_ecs::system::function_system::SystemParamFunction<fn() .> Out>>::run::call_inner at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:661:21 5: <Func as bevy_ecs::system::function_system::SystemParamFunction<fn() .> Out>>::run at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:664:17 6: <bevy_ecs::system::function_system::FunctionSystem<Marker,F> as bevy_ecs::system::system::System>::run_unsafe at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:504:19 7: bevy_ecs::schedule::executor::multi_threaded::ExecutorState::spawn_system_task::{{closure}}::{{closure}} at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs:621:26 8: core::ops::function::FnOnce::call_once at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/ops/function.rs:250:5 9: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panic/unwind_safe.rs:272:9 10: std::panicking::try::do_call at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:554:40 11: __rust_try 12: std::panicking::try at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:518:19 13: std::panic::catch_unwind at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panic.rs:142:14 14: bevy_ecs::schedule::executor::multi_threaded::ExecutorState::spawn_system_task::{{closure}} at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs:614:23 15: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::future::future::Future>::poll at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panic/unwind_safe.rs:297:9 16: <futures_lite::future::CatchUnwind<F> as core::future::future::Future>::poll::{{closure}} at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.2.0/src/future.rs:588:42 17: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panic/unwind_safe.rs:272:9 18: std::panicking::try::do_call at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:554:40 19: __rust_try 20: std::panicking::try at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:518:19 21: std::panic::catch_unwind at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panic.rs:142:14 22: <futures_lite::future::CatchUnwind<F> as core::future::future::Future>::poll at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.2.0/src/future.rs:588:9 23: async_executor::Executor::spawn::{{closure}} at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-executor-1.8.0/src/lib.rs:158:20 24: async_task::raw::RawTask<F,T,S,M>::run::{{closure}} at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-task-4.7.0/src/raw.rs:550:21 25: core::ops::function::FnOnce::call_once at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/ops/function.rs:250:5 26: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panic/unwind_safe.rs:272:9 27: std::panicking::try::do_call at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:554:40 28: __rust_try 29: std::panicking::try at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:518:19 30: std::panic::catch_unwind at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panic.rs:142:14 31: async_task::raw::RawTask<F,T,S,M>::run at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-task-4.7.0/src/raw.rs:549:23 32: async_task::runnable::Runnable<M>::run at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-task-4.7.0/src/runnable.rs:781:18 33: async_executor::Executor::run::{{closure}}::{{closure}} at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-executor-1.8.0/src/lib.rs:254:21 34: <futures_lite::future::Or<F1,F2> as core::future::future::Future>::poll at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.2.0/src/future.rs:449:33 35: async_executor::Executor::run::{{closure}} at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/async-executor-1.8.0/src/lib.rs:261:32 36: futures_lite::future::block_on::{{closure}} at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.2.0/src/future.rs:99:19 37: std:🧵:local::LocalKey<T>::try_with at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/thread/local.rs:286:16 38: std:🧵:local::LocalKey<T>::with at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/thread/local.rs:262:9 39: futures_lite::future::block_on at /home/vj/.cargo/registry/src/index.crates.io-6f17d22bba15001f/futures-lite-2.2.0/src/future.rs:78:5 40: bevy_tasks::task_pool::TaskPool::new_internal::{{closure}}::{{closure}}::{{closure}}::{{closure}} at /home/vj/workspace/rust/bevy/crates/bevy_tasks/src/task_pool.rs:180:37 41: std::panicking::try::do_call at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:554:40 42: __rust_try 43: std::panicking::try at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:518:19 44: std::panic::catch_unwind at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panic.rs:142:14 45: bevy_tasks::task_pool::TaskPool::new_internal::{{closure}}::{{closure}}::{{closure}} at /home/vj/workspace/rust/bevy/crates/bevy_tasks/src/task_pool.rs:174:43 46: std:🧵:local::LocalKey<T>::try_with at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/thread/local.rs:286:16 47: std:🧵:local::LocalKey<T>::with at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/thread/local.rs:262:9 48: bevy_tasks::task_pool::TaskPool::new_internal::{{closure}}::{{closure}} at /home/vj/workspace/rust/bevy/crates/bevy_tasks/src/task_pool.rs:167:25 note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. Encountered a panic in system `foo::main::{{closure}}`! Encountered a panic in system `bevy_app::main_schedule::Main::run_main`! get on your knees and beg mommy for forgiveness you pervert~ 💖 ``` </details> <details> <summary>Example output with this PR</summary> ``` Panic at src/main.rs:7:33: Foo stack backtrace: 0: rust_begin_unwind at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/std/src/panicking.rs:647:5 1: core::panicking::panic_fmt at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/panicking.rs:72:14 2: foo::main::{{closure}} at ./src/main.rs:7:59 3: core::ops::function::impls::<impl core::ops::function::FnMut<A> for &mut F>::call_mut at /rustc/8ace7ea1f7cbba7b4f031e66c54ca237a0d65de6/library/core/src/ops/function.rs:294:13 4: <Func as bevy_ecs::system::function_system::SystemParamFunction<fn() .> Out>>::run::call_inner at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:661:21 5: <Func as bevy_ecs::system::function_system::SystemParamFunction<fn() .> Out>>::run at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:664:17 6: <bevy_ecs::system::function_system::FunctionSystem<Marker,F> as bevy_ecs::system::system::System>::run_unsafe at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/system/function_system.rs:504:19 note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. Encountered a panic in system `foo::main::{{closure}}`! Encountered a panic in system `bevy_app::main_schedule::Main::run_main`! ``` </details> Full backtraces (`RUST_BACKTRACE=full`) are unchanged. ## Alternative solutions Write a custom panic hook. This could potentially let use exclude a few more callstack frames but requires a dependency on `backtrace` and is incompatible with user-provided panic hooks. --- ## Changelog - Backtraces now exclude many Bevy internals (unless `RUST_BACKTRACE=full` is used) --------- Co-authored-by: James Liu <contact@jamessliu.com> |
||
![]() |
c97d0103cc
|
Add typos - Source code spell checker (#12036)
# Objective - Avoid misspellings throughout the codebase by using [`typos`](https://github.com/crate-ci/typos) in CI Inspired by https://github.com/gfx-rs/wgpu/pull/5191 Typos is a minimal code speller written in rust that finds and corrects spelling mistakes among source code. - Fast enough to run on monorepos - Low false positives so you can run on PRs ## Solution - Use [typos-action](https://github.com/marketplace/actions/typos-action) in CI - Add how to use typos in the Contribution Guide --------- Co-authored-by: François <mockersf@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Joona Aalto <jondolf.dev@gmail.com> |
||
![]() |
42e61557f8
|
Immediately apply deferred system params in System::run (#11823)
# Objective Fixes #11821. ## Solution * Run `System::apply_deferred` in `System::run` after executing the system. * Switch to using `System::run_unsafe` in `SingleThreadedExecutor` to preserve the current behavior. * Remove the `System::apply_deferred` in `SimpleExecutor` as it's now redundant. * Remove the `System::apply_deferred` when running one-shot systems, as it's now redundant. --- ## Changelog Changed: `System::run` will now immediately apply deferred system params after running the system. ## Migration Guide `System::run` will now always run `System::apply_deferred` immediately after running the system now. If you were running systems and then applying their deferred buffers at a later point in time, you can eliminate the latter. ```rust // in 0.13 system.run(world); // .. sometime later ... system.apply_deferred(world); // in 0.14 system.run(world); ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
![]() |
9d13ae3113
|
Fix SimpleExecutor crash (#12076)
# Objective Since #9822, `SimpleExecutor` panics when an automatic sync point is inserted: ```rust let mut sched = Schedule::default(); sched.set_executor_kind(ExecutorKind::Simple); sched.add_systems((|_: Commands| (), || ()).chain()); sched.run(&mut World::new()); ``` ``` System's param_state was not found. Did you forget to initialize this system before running it? note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace Encountered a panic in system `bevy_ecs::schedule::executor::apply_deferred`! ``` ## Solution Don't try to run the `apply_deferred` system. |
||
![]() |
0dccfb5788
|
Stepping disabled performance fix (#11959)
# Objective * Fixes #11932 (performance impact when stepping is disabled) ## Solution The `Option<FixedBitSet>` argument added to `ScheduleExecutor::run()` in #8453 caused a measurable performance impact even when stepping is disabled. This can be seen by the benchmark of running `Schedule:run()` on an empty schedule in a tight loop (https://github.com/bevyengine/bevy/issues/11932#issuecomment-1950970236). I was able to get the same performance results as on 0.12.1 by changing the argument `ScheduleExecutor::run()` from `Option<FixedBitSet>` to `Option<&FixedBitSet>`. The down-side of this change is that `Schedule::run()` now takes about 6% longer (3.7319 ms vs 3.9855ns) when stepping is enabled --- ## Changelog * Change `ScheduleExecutor::run()` `_skipped_systems` from `Option<FixedBitSet>` to `Option<&FixedBitSet>` * Added a few benchmarks to measure `Schedule::run()` performance with various executors |
||
![]() |
5c52d0aeee
|
System Stepping implemented as Resource (#8453)
# Objective Add interactive system debugging capabilities to bevy, providing step/break/continue style capabilities to running system schedules. * Original implementation: #8063 - `ignore_stepping()` everywhere was too much complexity * Schedule-config & Resource discussion: #8168 - Decided on selective adding of Schedules & Resource-based control ## Solution Created `Stepping` Resource. This resource can be used to enable stepping on a per-schedule basis. Systems within schedules can be individually configured to: * AlwaysRun: Ignore any stepping state and run every frame * NeverRun: Never run while stepping is enabled - this allows for disabling of systems while debugging * Break: If we're running the full frame, stop before this system is run Stepping provides two modes of execution that reflect traditional debuggers: * Step-based: Only execute one system at a time * Continue/Break: Run all systems, but stop before running a system marked as Break ### Demo https://user-images.githubusercontent.com/857742/233630981-99f3bbda-9ca6-4cc4-a00f-171c4946dc47.mov Breakout has been modified to use Stepping. The game runs normally for a couple of seconds, then stepping is enabled and the game appears to pause. A list of Schedules & Systems appears with a cursor at the first System in the list. The demo then steps forward full frames using the spacebar until the ball is about to hit a brick. Then we step system by system as the ball impacts a brick, showing the cursor moving through the individual systems. Finally the demo switches back to frame stepping as the ball changes course. ### Limitations Due to architectural constraints in bevy, there are some cases systems stepping will not function as a user would expect. #### Event-driven systems Stepping does not support systems that are driven by `Event`s as events are flushed after 1-2 frames. Although game systems are not running while stepping, ignored systems are still running every frame, so events will be flushed. This presents to the user as stepping the event-driven system never executes the system. It does execute, but the events have already been flushed. This can be resolved by changing event handling to use a buffer for events, and only dropping an event once all readers have read it. The work-around to allow these systems to properly execute during stepping is to have them ignore stepping: `app.add_systems(event_driven_system.ignore_stepping())`. This was done in the breakout example to ensure sound played even while stepping. #### Conditional Systems When a system is stepped, it is given an opportunity to run. If the conditions of the system say it should not run, it will not. Similar to Event-driven systems, if a system is conditional, and that condition is only true for a very small time window, then stepping the system may not execute the system. This includes depending on any sort of external clock. This exhibits to the user as the system not always running when it is stepped. A solution to this limitation is to ensure any conditions are consistent while stepping is enabled. For example, all systems that modify any state the condition uses should also enable stepping. #### State-transition Systems Stepping is configured on the per-`Schedule` level, requiring the user to have a `ScheduleLabel`. To support state-transition systems, bevy generates needed schedules dynamically. Currently it’s very difficult (if not impossible, I haven’t verified) for the user to get the labels for these schedules. Without ready access to the dynamically generated schedules, and a resolution for the `Event` lifetime, **stepping of the state-transition systems is not supported** --- ## Changelog - `Schedule::run()` updated to consult `Stepping` Resource to determine which Systems to run each frame - Added `Schedule.label` as a `BoxedSystemLabel`, along with supporting `Schedule::set_label()` and `Schedule::label()` methods - `Stepping` needed to know which `Schedule` was running, and prior to this PR, `Schedule` didn't track its own label - Would have preferred to add `Schedule::with_label()` and remove `Schedule::new()`, but this PR touches enough already - Added calls to `Schedule.set_label()` to `App` and `World` as needed - Added `Stepping` resource - Added `Stepping::begin_frame()` system to `MainSchedulePlugin` - Run before `Main::run_main()` - Notifies any `Stepping` Resource a new render frame is starting ## Migration Guide - Add a call to `Schedule::set_label()` for any custom `Schedule` - This is only required if the `Schedule` will be stepped --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
![]() |
b2661ea73d
|
Reorder impl to be the same as the trait (#10964)
# Objective - Make the implementation order consistent between all sources to fit the order in the trait. ## Solution - Change the implementation order. |
||
![]() |
324c057b71
|
Cache System Tracing Spans (#9390)
# Objective - Reduce the overhead of tracing by caching the system spans. Yellow is this pr. Red is main.  |
||
![]() |
32faf4cb5c
|
Document every public item in bevy_ecs (#8731)
# Objective Title. --------- Co-authored-by: François <mockersf@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: James Liu <contact@jamessliu.com> |
||
![]() |
cbd4abf0fc
|
Rename apply_system_buffers to apply_deferred (#8726)
# Objective - `apply_system_buffers` is an unhelpful name: it introduces a new internal-only concept - this is particularly rough for beginners as reasoning about how commands work is a critical stumbling block ## Solution - rename `apply_system_buffers` to the more descriptive `apply_deferred` - rename related fields, arguments and methods in the internals fo bevy_ecs for consistency - update the docs ## Changelog `apply_system_buffers` has been renamed to `apply_deferred`, to more clearly communicate its intent and relation to `Deferred` system parameters like `Commands`. ## Migration Guide - `apply_system_buffers` has been renamed to `apply_deferred` - the `apply_system_buffers` method on the `System` trait has been renamed to `apply_deferred` - the `is_apply_system_buffers` function has been replaced by `is_apply_deferred` - `Executor::set_apply_final_buffers` is now `Executor::set_apply_final_deferred` - `Schedule::apply_system_buffers` is now `Schedule::apply_deferred` --------- Co-authored-by: JoJoJet <21144246+JoJoJet@users.noreply.github.com> |
||
![]() |
55e9ab7c92
|
Cleaned up panic messages (#8219)
# Objective Fixes #8215 and #8152. When systems panic, it causes the main thread to panic as well, which clutters the output. ## Solution Resolves the panic in the multi-threaded scheduler. Also adds an extra message that tells the user the system that panicked. Using the example from the issue, here is what the messages now look like: ```rust use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) .add_systems(Update, panicking_system) .run(); } fn panicking_system() { panic!("oooh scary"); } ``` ### Before ``` Compiling bevy_test v0.1.0 (E:\Projects\Rust\bevy_test) Finished dev [unoptimized + debuginfo] target(s) in 2m 58s Running `target\debug\bevy_test.exe` 2023-03-30T22:19:09.234932Z INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Windows 10 Pro", kernel: "19044", cpu: "AMD Ryzen 5 2600 Six-Core Processor", core_count: "6", memory: "15.9 GiB" } thread 'Compute Task Pool (5)' panicked at 'oooh scary', src\main.rs:11:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'Compute Task Pool (5)' panicked at 'A system has panicked so the executor cannot continue.: RecvError', E:\Projects\Rust\bevy\crates\bevy_ecs\src\schedule\executor\multi_threaded.rs:194:60 thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', E:\Projects\Rust\bevy\crates\bevy_tasks\src\task_pool.rs:376:49 error: process didn't exit successfully: `target\debug\bevy_test.exe` (exit code: 101) ``` ### After ``` Compiling bevy_test v0.1.0 (E:\Projects\Rust\bevy_test) Finished dev [unoptimized + debuginfo] target(s) in 2.39s Running `target\debug\bevy_test.exe` 2023-03-30T22:11:24.748513Z INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Windows 10 Pro", kernel: "19044", cpu: "AMD Ryzen 5 2600 Six-Core Processor", core_count: "6", memory: "15.9 GiB" } thread 'Compute Task Pool (5)' panicked at 'oooh scary', src\main.rs:11:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace Encountered a panic in system `bevy_test::panicking_system`! Encountered a panic in system `bevy_app::main_schedule::Main::run_main`! error: process didn't exit successfully: `target\debug\bevy_test.exe` (exit code: 101) ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François <mockersf@gmail.com> |
||
![]() |
aa4170d9a4 |
Rename schedule v3 to schedule (#7519)
# Objective - Follow up of https://github.com/bevyengine/bevy/pull/7267 ## Solution - Rename schedule_v3 to schedule - Suppress "module inception" lint |