Compare commits

...

530 Commits

Author SHA1 Message Date
François Mockers
37ad0cd28c exlude no_std_library when updating 2025-06-21 23:18:35 +02:00
François Mockers
8d89f23dfc
keep no_std_library to 0.1.0 2025-06-21 23:16:58 +02:00
François Mockers
c574179266 trigger CI 2025-06-21 23:09:08 +02:00
Bevy Auto Releaser
670b35400f chore: Release 2025-06-21 21:07:00 +00:00
eugineerd
2d897380a0
add missing #[track_caller] (#19769)
# Objective

I've noticed that some methods with `MaybeLocation::caller` don't have
`#[track_caller]` which resulted in wrong locations reported when
`track_location` is enabled.

## Solution

add `#[track_caller]` to them.
2025-06-21 18:05:58 +00:00
Talin
4ff595a4bc
New simplified "click to focus" logic for core widgets. (#19736)
Click to focus is now a global observer.

# Objective

Previously, the "click to focus" behavior was implemented in each
individual headless widget, producing redundant logic.

## Solution

The new scheme is to have a global observer which looks for pointer down
events and triggers an `AcquireFocus` event on the target. This event
bubbles until it finds an entity with `TabIndex`, and then focuses it.

## Testing

Tested the changes using the various examples that have focusable
widgets. (This will become easier to test when I add focus ring support
to the examples, but that's for another day. For now you just have to
know which keys to press.)

## Migration

This change is backwards-compatible. People who want the new behavior
will need to install the new plugin.
2025-06-21 18:05:26 +00:00
Jan Hohenheim
d770648c2f
Remove unused code in Perlin noise 1D impl (#19765)
# Objective

- Fix #19759 
- The bigger permutation table only comes into play for higher
dimensions than 1, when you start doing
`PERMUTATION_TABLE[PERMUTATION_TABLE[index] + some_number]`
- The bigger permutation table has no mathematical meaning, it's just
there to avoid having to write more `& 0xFF` when doing multiple nested
lookups in higher dimensions
- But we only use 1D Perlin noise for the camera shake because we want
the dimensions to be uncorrelated

## Solution

- So, we can trim the permutation table down :) 
- This should be mathematically identical, as a wrapped value will still
access the same element as an unwrapped value would in the bigger table
- The comment was a bit misleading anyways. "mirror" did not refer to
"mirrored values" but to "repeated values".

## Testing

- Ran the example. Still behaves like before.
2025-06-21 15:07:43 +00:00
ickshonpe
45a3f3d138
Color interpolation in OKLab, OKLCH spaces for UI gradients (#19330)
# Objective

Add support for interpolation in OKLab and OKLCH color spaces for UI
gradients.

## Solution
* New `InterpolationColorSpace` enum with `OkLab`, `OkLch`, `OkLchLong`,
`Srgb` and `LinearRgb` variants.
  * Added a color space specialization to the gradients pipeline.
* Added support for interpolation in OkLCH and OkLAB color spaces to the
gradients shader. OKLCH interpolation supports both short and long hue
paths. This is mostly based on the conversion functions from
`bevy_color` except that interpolation in polar space uses radians.
  * Added `color_space` fields to each gradient type.

## Testing

The `gradients` example has been updated to demonstrate the different
color interpolation methods.
Press space to cycle through the different options.

---

## Showcase


![color_spaces](https://github.com/user-attachments/assets/e10f8342-c3c8-487e-b386-7acdf38d638f)
2025-06-21 15:06:35 +00:00
Lailatova
5d5a95fa6e
Fix issue 19734: add dependency on bevy_utils for the bevy_ecs test. (#19738)
Without this dependency, the bevy_ecs tests fail with missing as_string
methods.

# Objective

 - Fixes #19734

## Solution

- add bevy_utils with feature = "Debug" to dev-dependencies 

## Testing

- Ran `cargo test -p bevy_ecs`
- Ran `taplo fmt --check`

---
2025-06-21 15:05:04 +00:00
Lucas Franca
8f08d6bc86
Move usages folder to usage (#19757)
# Objective

There were 2 folders inside of `examples`, each with 1 example, and with
similar folder names.

## Solution

Move the example in the `usages` folder to `usage`.

## Testing
 
`cargo run -p ci`
2025-06-21 00:06:44 +00:00
François Mockers
9ed928b071
don't allow nightly lints (#19756)
# Objective

- A nightly only lint is allowed in cargo.toml, making all stable builds
issue warning
- Fixes #19528 

## Solution

- Don't allow nightly lints
2025-06-20 23:44:31 +00:00
MevLyshkin
84f21f7c8a
Schema types metadata (#19524)
# Objective

- Currently there is predefinied list of supported DataTypes that can be
detected on Bevy JSON Schema generation and mapped as reflect_types
array elements.
- Make it possible to register custom `reflectTypes` mappings for Bevy
JSON Schema.

## Solution

- Create a `SchemaTypesMetadata` Resource that will hold mappings for
`TypeId` of `TypeData`. List is bigger from beggining and it is possible
to expand it without forking package.

## Testing

- I use it for quite a while in my game, I have a fork of bevy_remote
with more changes that later I want to merge to main as well.

---------

Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2025-06-20 17:07:16 +00:00
Jan Hohenheim
ffd6c9e1c9
Rewrite camera shake example (#19724)
# Objective

- Alternative to #19721
- The old implementation had several issues:
  - underexplained
  - bit complicated in places
  - did not follow the source as described
    - camera moves away
    - camera does not use noise
- camera nudges back after shake ends, which looks cinematic, but not
what you want in practice

All in all: the old implementation did not really show a typical
implementation IMO

## Solution

- Rewrite it :D
- I believe the current implementation is a robust thing you can learn
from or just copy-paste into your project

## Testing


https://github.com/user-attachments/assets/bfe74fb6-c428-4d5a-9c9c-cd4a034ba176

---------

Co-authored-by: Rob Parrett <robparrett@gmail.com>
2025-06-20 16:43:14 +00:00
Talin
9fdddf7089
Core Checkbox (#19665)
# Objective

This is part of the "core widgets" effort:
https://github.com/bevyengine/bevy/issues/19236.

## Solution

This adds the "core checkbox" widget type.

## Testing

Tested using examples core_widgets and core_widgets_observers.

Note to reviewers: I reorganized the code in the examples, so the diffs
are large because of code moves.
2025-06-20 16:37:18 +00:00
Giacomo Stevanato
35166d9029
Refactor bundle derive (#19749)
# Objective

- Splitted off from  #19491
- Make adding generated code to the `Bundle` derive macro easier
- Fix a bug when multiple fields are `#[bundle(ignore)]`

## Solution

- Instead of accumulating the code for each method in a different `Vec`,
accumulate only the names of non-ignored fields and their types, then
use `quote` to generate the code for each of them in the method body.
- To fix the bug, change the code populating the `BundleFieldKind` to
push only one of them per-field (previously each `#[bundle(ignore)]`
resulted in pushing twice, once for the correct
`BundleFieldKind::Ignore` and then again unconditionally for
`BundleFieldKind::Component`)

## Testing

- Added a regression test for the bug that was fixed
2025-06-20 16:36:08 +00:00
Emerson Coskey
8e1d0051d2
Fix QueryData derive codegen (#19750)
Custom derived `QueryData` impls currently generate `Item` structs with
the lifetimes swapped, which blows up the borrow checker sometimes.

See:
https://discord.com/channels/691052431525675048/749335865876021248/1385509416086011914

could add a regression test, TBH I don't know the error well enough to
do that minimally. Seems like it's that both lifetimes on
`QueryData::Item` need to be covariant, but I'm not sure.
2025-06-20 15:48:16 +00:00
theotherphil
b6bd205e8a
Gate Reflect derive behind feature flag (#19745)
# Objective

Fix https://github.com/bevyengine/bevy/issues/19733

## Solution

Gate reflect features behind `feature(bevy_reflect)`

## Tests

None
2025-06-19 21:58:17 +00:00
theotherphil
d2e1f725db
deny(missing_docs) for bevy_window (#19655)
# Objective

Write some more boilerplate-y docs, to get one crate closer to closing
https://github.com/bevyengine/bevy/issues/3492.
2025-06-19 19:33:49 +00:00
theotherphil
b129764924
deny(missing_docs) for bevy_asset_macros (#19719)
# Objective

More boilerplate docs, towards
https://github.com/bevyengine/bevy/issues/3492.

Another place where https://github.com/bevyengine/bevy/issues/19537
might be worth doing as a follow up - I've added a comment on that
issue.
2025-06-19 02:02:39 +00:00
Chris Russell
d0550f58ae
Remove 's lifetime from WorldQuery::Fetch (#19720)
# Objective

Unblock #18162.

#15396 added the `'s` lifetime to `QueryData::Item` to make it possible
for query items to borrow from the state. The state isn't passed
directly to `QueryData::fetch()`, so it also added the `'s` lifetime to
`WorldQuery::Fetch` so that we can pass the borrows through there.

Unfortunately, having `WorldQuery::Fetch` borrow from the state makes it
impossible to have owned state, because we store the state and the
`Fetch` in the same `struct` during iteration.

## Solution

Undo the change to add the `'s` lifetime to `WorldQuery::Fetch`.

Instead, add a `&'s Self::State` parameter to `QueryData::fetch()` and
`QueryFilter::filter_fetch()` so that borrows from the state can be
passed directly to query items.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Emerson Coskey <emerson@coskey.dev>
2025-06-19 00:58:21 +00:00
François Mockers
4e694aea53
ECS: put strings only used for debug behind a feature (#19558)
# Objective

- Many strings in bevy_ecs are created but only used for debug: system
name, component name, ...
- Those strings make a significant part of the final binary and are no
use in a released game

## Solution

- Use [`strings`](https://linux.die.net/man/1/strings) to find ...
strings in a binary
- Try to find where they come from
- Many are made from `type_name::<T>()` and only used in error / debug
messages
- Add a new structure `DebugName` that holds no value if `debug` feature
is disabled
- Replace `core::any::type_name::<T>()` by `DebugName::type_name::<T>()`

## Testing

Measurements were taken without the new feature being enabled by
default, to help with commands

### File Size

I tried building the `breakout` example with `cargo run --release
--example breakout`

|`debug` enabled|`debug` disabled|
|-|-|
|81621776 B|77735728B|
|77.84MB|74.13MB|

### Compilation time

`hyperfine --min-runs 15 --prepare "cargo clean && sleep 5"
'RUSTC_WRAPPER="" cargo build --release --example breakout'
'RUSTC_WRAPPER="" cargo build --release --example breakout --features
debug'`

```
breakout' 'RUSTC_WRAPPER="" cargo build --release --example breakout --features debug'
Benchmark 1: RUSTC_WRAPPER="" cargo build --release --example breakout
  Time (mean ± σ):     84.856 s ±  3.565 s    [User: 1093.817 s, System: 32.547 s]
  Range (min … max):   78.038 s … 89.214 s    15 runs

Benchmark 2: RUSTC_WRAPPER="" cargo build --release --example breakout --features debug
  Time (mean ± σ):     92.303 s ±  2.466 s    [User: 1193.443 s, System: 33.803 s]
  Range (min … max):   90.619 s … 99.684 s    15 runs

Summary
  RUSTC_WRAPPER="" cargo build --release --example breakout ran
    1.09 ± 0.05 times faster than RUSTC_WRAPPER="" cargo build --release --example breakout --features debug
```
2025-06-18 20:15:25 +00:00
Kristoffer Søholm
2119838e27
Add support for ButtonInput<Key> (#19684)
# Objective

While `KeyCode` is very often the correct way to interact with keyboard
input there are a bunch of cases where it isn't, notably most of the
symbols (e.g. plus, minus, different parentheses). Currently the only
way to get these is to read from `EventReader<KeyboardInput>`, but then
you'd have to redo the `ButtonInput` logic for pressed/released to e.g.
make zoom functionality that depends on plus/minus keys.

This has led to confusion previously, like
https://github.com/bevyengine/bevy/issues/3278

## Solution

Add a `ButtonInput<Key>` resource.

## Testing

Modified the `keyboard_input` example to test it.

## Open questions

I'm not 100% sure this is the right way forward, since it duplicates the
key processing logic and might make people use the shorter
`ButtonInput<Key>` even when it's not appropriate.

Another option is to add a new struct with both `Key` and `KeyCode`, and
use `ButtonInput` with that instead. That would make it more
explanatory, but that is a lot of churn.

The third alternative is to not do this because it's too niche.

I'll add more documentation and take it out of draft if we want to move
forward with it.
2025-06-18 20:15:03 +00:00
atlv
a1d3c6197f
rename Transform::compute_matrix to to_matrix (#19646)
# Objective

- Parity with #19643

## Solution

- Rename

## Testing

- None

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-18 05:37:25 +00:00
Lucas Franca
6f08bb84d2
Exposes Observer's system's name (#19611)
# Objective

Fixes #18726
Alternative to and closes #18797

## Solution

Create a method `Observer::system_name` to expose the name of the
`Observer`'s system

## Showcase

```rust
// Returns `my_crate::my_observer`
let observer = Observer::new(my_observer);
println!(observer.system_name());

// Returns `my_crate::method::{{closure}}`
let observer = Observer::new(|_trigger: Trigger<...>|);
println!(observer.system_name());

// Returns `custom_name`
let observer = Observer::new(IntoSystem::into_system(my_observer).with_name("custom_name"));
println!(observer.system_name());
```

## TODO
- [ ] Achieve cart's approval

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-17 22:38:31 +00:00
Malek
f3e7a4b048
Added clone bounds to EntityEvents that were missing them. (#19708)
# Objective

Lack of clone on certain builtin events

## Solution

Added clone to those events

## Testing

## Showcase
2025-06-17 21:22:32 +00:00
theotherphil
66086a2616
Add TypeIdMapExt trait to make TypeIdMap operations more ergonomic (#19683)
# Objective
 
Fix https://github.com/bevyengine/bevy/issues/19642 by enabling e.g.

```
map.get_type::<MyType>();
```

in place of

```
map.get(&TypeId::of::<MyType>());
```

## Solution

Add an extension trait `TypeIdMapExt` with `insert_type`, `get_type`,
`get_type_mut` and `remove_type` counterparts for `insert`, `get`,
`get_mut` and `remove`.

## Testing

Doc test.
2025-06-17 21:06:27 +00:00
Alice Cecile
1079b83af9
Revert "bevy_log: refactor how log layers are wired together (#19248)" (#19705)
This reverts commit 8661e914a5, aka
#19248.

Fixes #19689.
2025-06-17 20:41:30 +00:00
Jan Hohenheim
a750cfe4a1
Split CursorOptions off of Window (#19668)
# Objective

- Fixes #19627 
- Tackles part of #19644 
- Supersedes #19629
- `Window` has become a very very very big component
- As such, our change detection does not *really* work on it, as e.g.
moving the mouse will cause a change for the entire window
- We circumvented this with a cache
- But, some things *shouldn't* be cached as they can be changed from
outside the user's control, notably the cursor grab mode on web
- So, we need to disable the cache for that
- But because change detection is broken, that would result in the
cursor grab mode being set every frame the mouse is moved
- That is usually *not* what a dev wants, as it forces the cursor to be
locked even when the end-user is trying to free the cursor on the
browser
  - the cache in this situation is invalid due to #8949

## Solution

- Split `Window` into multiple components, each with working change
detection
- Disable caching of the cursor grab mode
- This will only attempt to force the grab mode when the `CursorOptions`
were touched by the user, which is *much* rarer than simply moving the
mouse.
- If this PR is merged, I'll do the exact same for the other
constituents of `Window` as a follow-up

## Testing

- Ran all the changed examples
2025-06-17 20:20:13 +00:00
Alejandro Pascual
d1c6fbea57
Support fallible one-shot systems (#19678)
Closes #19677.

I don't think that the output type needs to be `Send`. I've done some
test at it seems to work fine without it, which in IMO makes sense, but
please correct me if that is not the case.
2025-06-17 19:48:37 +00:00
atlv
2915a3b903
rename GlobalTransform::compute_matrix to to_matrix (#19643)
# Objective

- compute_matrix doesn't compute anything, it just puts an Affine3A into
a Mat4. the name is inaccurate

## Solution

- rename it to conform with to_isometry (which, ironically, does compute
a decomposition which is rather expensive)

## Testing

- Its a rename. If it compiles, its good to go

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-17 18:37:26 +00:00
Erick Z
20781371e8
Register ReflectSerialize for &'static str (#19680)
# Objective

- When trying to serialize an structure that contains `&'static str`
using only Reflection, I get the following error:
```
"type `&str` did not register the `ReflectSerialize` or `ReflectSerializeWithRegistry` type data. 
For certain types, this may need to be registered manually using `register_type_data` (stack: ... -> `core::option::Option<&str>` -> `&str`)")
```
## Solution

- Register `ReflectSerialize` for `&str`

## Testing

- `cargo run -p ci`: OK
2025-06-16 22:09:15 +00:00
ickshonpe
c0fa10b0c3
slider widget track click position UiScale fix (#19676)
# Objective

The position for track clicks in `core_slider` is calculated incorrectly
when using `UiScale`.

## Solution

`trigger.event().pointer_location.position` uses logical window
coordinates, that is:

`position = physical_position / window_scale_factor`

while `ComputedNodeTarget::scale_factor` returns the window scale factor
multiplied by Ui Scale:

`target_scale_factor = window_scale_factor * ui_scale`

So to get the physical position need to divide by the `UiScale`:

```
position * target_scale_factor / ui_scale
= (physical_postion / window_scale_factor) * (window_scale_factor * ui_scale) / ui_scale
=  physical_position
```

I thought this was fixed during the slider PR review, but must have got
missed somewhere or lost in a merge.

## Testing

Can test using the `core_widgets` example` with
`.insert_resource(UiScale(2.))` added to the bevy app.
2025-06-16 22:07:54 +00:00
HippoGamus
209866cc27
Fix Keyboard observer not updating SliderValue (#19661)
# Objective

When the `CoreSlider`s `on_change` is set to None, Keyboard input, like
ArrowKeys, does not update the `SliderValue`.

## Solution

Handle the missing case, like it is done for Pointer.

## Testing

- Did you test these changes?
Yes: core_widgets & core_widgets_observers
in both examples one has to remove / comment out the setting of
`CoreSlider::on_change` to test the case of `on_change` being none.

- Are there any parts that need more testing?
No not that I am aware of.

- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
Yes: core_widgets & core_widgets_observers
in both examples one has to remove / comment out the setting of
`CoreSlider::on_change` to test the case of `on_change` being none.

- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
I tested on linux + wayland. But it is unlikely that it would effect
outcomes.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-16 22:05:19 +00:00
atlv
7c2289c96e
Clarify GlobalTransform::to_isometry doc (#19645)
# Objective

- to_isometry is not a direct conversion, it involves computation. the
docs could be clearer

## Solution

- Improve docs

## Testing

- its docs
2025-06-16 22:02:30 +00:00
Alice Cecile
7237c2173b
Make observers metadata / storage publicly visible (#19608)
# Objective

Our strategy for storing observers is made up of several moving parts,
which are ultimately fairly simple nested HashMaps.

These types are currently `pub`, but lack any meaningful way to access
this data.

We have three options here: 

1. Make these internals not `pub` at all.
2. Make the data read-only accessible.
3. Make the data mutably accessible.

## Solution

I've opted for option 2, exposing read-only values. This is consistent
with our existing approach to the ECS internals, allowing for easier
debugging without risking wanton data corruption. If one day you would
like to mutably access this data, please open an issue clearly
explaining what you're trying to do.

This was a pretty mechanical change, exposing fields via getters. I've
also opted to do my best to clarify some field names and documentation:
please double-check those for correctness. It was hard to be fully
confident, as the field names and documentation was not very clear ;)

## Testing

I spent some time going through the code paths, making sure that users
can trace all the way from `World` to the leaf nodes. Reviewers, please
ensure the same!

## Notes for reviewers

This is part of a broader observer overhaul: I fully expect us to change
up these internals and break these shiny new APIs. Probably even within
the same cycle!

But clean up your work area first: this sort of read-only getter and
improved docs will be important to replace as we work.
2025-06-16 22:00:33 +00:00
ickshonpe
0c63aa2405
Document TextShadow not supported by Text2d (#19533)
# Objective

Document `TextShadow` not supported by `Text2d`

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-16 21:55:32 +00:00
Jan Hohenheim
9b743d2a43
Allow users to fix glTF coordinate system imports (#19633)
# Objective

*Fixes #5670 as an opt-in for now*

glTF uses the following coordinate system:

- forward: Z
- up: Y
- right: -X

and Bevy uses:

- forward: -Z
- up: Y
- right: X

For the longest time, Bevy has simply ignored this distinction. That
caused issues when working across programs, as most software respects
the
glTF coordinate system when importing and exporting glTFs. Your scene
might have looked correct in Blender, Maya, TrenchBroom, etc. but
everything would be flipped when importing it into Bevy!

## Solution

Add an option to the glTF loader to perform coordinate conversion. Note
that this makes a distinction in the camera nodes, as glTF uses a
different coordinate system for them.

## Follow Ups

- Add global glTF loader settings, similar to the image loader, so that
users can make third-party crates also load their glTFs with corrected
coordinates
- Decide on a migration strategy to make this the future default
  - Create an issue
- Get feedback from Patrick Walton and Cart (not pinging them here to
not spam them)
  - Include this pic for reference of how Blender assumes -Y as forward:

![image](https://github.com/user-attachments/assets/8f5ae364-48f0-46e4-922b-50bccb8d58b3)

## Testing

I ran all glTF animation examples with the new setting enabled to
validate that they look the same, just flipped.

Also got a nice test scene from Chris that includes a camera inside the
glTF. Thanks @ChristopherBiscardi!

Blender (-Y forward): 

![image](https://github.com/user-attachments/assets/129013f1-a025-488a-8764-c7ee5e7019a1)

Bevy (-Z forward, but the model looks the wrong way):

![image](https://github.com/user-attachments/assets/842e00e0-48ce-4ca7-a88e-ea458ecbf852)

Bevy with `convert_coordinates` enabled (-Z forward):

![image](https://github.com/user-attachments/assets/e97f3797-75a0-4d2b-ac54-130ba69f0a3c)

Validation that the axes are correct with F3D's glTF viewer (+Z
forward):

![image](https://github.com/user-attachments/assets/b9f02adf-a7b0-4a18-821f-fdd04426d3bd)
2025-06-16 21:47:34 +00:00
mgi388
1f2fd3d29d
Fix SubStates with multiple source states not reacting to all source changes (#19595)
# Objective

- Fix issue where `SubStates` depending on multiple source states would
only react when _all_ source states changed simultaneously.
- SubStates should be created/destroyed whenever _any_ of their source
states transitions, not only when all change together.

# Solution

- Changed the "did parent change" detection logic from AND to OR. We
need to check if _any_ of the event readers changed, not if _all_ of
them changed.
- See
https://github.com/bevyengine/bevy/actions/runs/15610159742/job/43968937544?pr=19595
for failing test proof before I pushed the fix.
- The generated code we want needs `||`s not `&&`s like this:

```rust
fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(schedule: &mut Schedule) {
  let apply_state_transition = |(mut ereader0, mut ereader1, mut ereader2): (
      EventReader<StateTransitionEvent<S0::RawState>>,
      EventReader<StateTransitionEvent<S1::RawState>>,
      EventReader<StateTransitionEvent<S2::RawState>>,
  ),
      event: EventWriter<StateTransitionEvent<T>>,
      commands: Commands,
      current_state_res: Option<ResMut<State<T>>>,
      next_state_res: Option<ResMut<NextState<T>>>,
      (s0, s1, s2): (
          Option<Res<State<S0::RawState>>>,
          Option<Res<State<S1::RawState>>>,
          Option<Res<State<S2::RawState>>>,
  )| {
    // With `||` we can correctly count parent changed if any of the sources changed.
    let parent_changed = (ereader0.read().last().is_some()
        || ereader1.read().last().is_some()
        || ereader2.read().last().is_some());
    let next_state = take_next_state(next_state_res);
    if !parent_changed && next_state.is_none() {
        return;
    }
    // ...
  }
}
```

# Testing

- Add new test.
- Check the fix worked in my game.
2025-06-16 21:34:22 +00:00
SilentSpaceTraveller
8661e914a5
bevy_log: refactor how log layers are wired together (#19248)
# Objective

Current way to wire `Layer`s together using `layer.with(new_layer)` in
the `bevy_log` plugin is brittle and not flexible. As #17722
demonstrated, the current solution makes it very hard to do any kind of
advanced wiring, as the type system of `tracing::Subscriber` gets in the
way very quickly (the type of each new layer depends on the type of the
previous ones). We want to make it easier to have more complex wiring of
`Layers`. It would be hard to solve #19085 without it

## Solution
It aims to be functionally equivalent.
- Replace of using `layer.with(new_layer)` . We now add `layer.boxed()`
to a `Vec<BoxedLayer>`. It is a solution recommended by
`tracing_subscriber::Layer` for complex wiring cases (See
https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/index.html#runtime-configuration-with-layers)
- Do some refactoring and clean up that is now enabled by the new
solution

## Testing
- Ran CI locally on Linux
- Ran the logs examples
- Need people familiar with the features `trace`, `tracing-chrome`,
`tracing-tracy` to check that it still works as expected
- Need people with access to `ios`, `android` and `wasm` to check it as
well.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
2025-06-16 21:30:55 +00:00
theotherphil
3b25b38bdc
deny(missing_docs) for bevy_reflect (#19481)
## Objective

Deny missing docs in bevy_reflect, towards
https://github.com/bevyengine/bevy/issues/3492.

## Solution

Add the missing docs!

## Testing

N/A
2025-06-16 21:26:24 +00:00
Austreelis
6db71367d4
allow access to the source error of AssetLoaderError and downcasting (#19471)
# Objective

I have a custom asset loader, and need access to the error it reports
when failing to load (e.g. through `AssetLoadFailedEvent { error:
AssetLoadError::AssetLoaderError(loader_error), .. }`). However
`AssetLoaderError` doesn't expose its `<core::error::Error>::source()`
(i.e. its `error` field. It only formats it when `Display`ed.

*I haven't searched for issues about it.*

## Solution

- Annotate `AssetLoaderError`'s `error` field with `#[source]`.
- Don't include the error when `AssetLoaderError` is `Display`ed (when
one prints an error's source stack like a backtrace, it would now be
dupplicated).
- (optional, included as a separated commit) Add a getter for the `&dyn
Error` stored in the `error` field (whithin an `Arc`). This is more
ergonomic than using `Error::source()` because it casts an `&Arc<dyn
Error>` into an `&dyn Error`, meaning one has to downcast it twice to
get the original error from the loader, including once where you have to
specify the correct type of the *private* `error` field. So downcasting
from `Error::source()` effectively rely on the internal implementation
of `AssetLoaderError`. The getter instead return the trait object
directly, which mean it will directly downcast to the expected loader
error type.

I didn't included a test that checks that double-downcasting
`<AssetLoaderError as Error>::source()` doesn't break user code that
would rely on the private field's type.

## Testing

- Downcasting the trait objects for both `source()` and the `error()`
getter work as described above.
- `cargo test -p bevy_asset --all-features` pass without errors.

---------

Co-authored-by: austreelis <git@swhaele.net>
2025-06-16 21:24:01 +00:00
Lucas Franca
c8cb7bdf57
Allow passing number of thread for building and testing to CI (#19359)
# Objective

Fixes #16051
Closes #16145

## Solution

Allow passing `--build-jobs` and `--test-threads` to `ci`
i.e.
```
cargo run -p ci -- --build-jobs 4 --test-threads 4
```

## Testing

running ci locally

---------

Co-authored-by: Benjamin Brienen <Benjamin.Brienen@outlook.com>
2025-06-16 21:19:47 +00:00
Chris Russell
f7e112a3c9
Let query items borrow from query state to avoid needing to clone (#15396)
# Objective

Improve the performance of `FilteredEntity(Ref|Mut)` and
`Entity(Ref|Mut)Except`.

`FilteredEntityRef` needs an `Access<ComponentId>` to determine what
components it can access. There is one stored in the query state, but
query items cannot borrow from the state, so it has to `clone()` the
access for each row. Cloning the access involves memory allocations and
can be expensive.


## Solution

Let query items borrow from their query state.  

Add an `'s` lifetime to `WorldQuery::Item` and `WorldQuery::Fetch`,
similar to the one in `SystemParam`, and provide `&'s Self::State` to
the fetch so that it can borrow from the state.

Unfortunately, there are a few cases where we currently return query
items from temporary query states: the sorted iteration methods create a
temporary state to query the sort keys, and the
`EntityRef::components<Q>()` methods create a temporary state for their
query.

To allow these to continue to work with most `QueryData`
implementations, introduce a new subtrait `ReleaseStateQueryData` that
converts a `QueryItem<'w, 's>` to `QueryItem<'w, 'static>`, and is
implemented for everything except `FilteredEntity(Ref|Mut)` and
`Entity(Ref|Mut)Except`.

`#[derive(QueryData)]` will generate `ReleaseStateQueryData`
implementations that apply when all of the subqueries implement
`ReleaseStateQueryData`.

This PR does not actually change the implementation of
`FilteredEntity(Ref|Mut)` or `Entity(Ref|Mut)Except`! That will be done
as a follow-up PR so that the changes are easier to review. I have
pushed the changes as chescock/bevy#5.

## Testing

I ran performance traces of many_foxes, both against main and against
chescock/bevy#5, both including #15282. These changes do appear to make
generalized animation a bit faster:

(Red is main, yellow is chescock/bevy#5)

![image](https://github.com/user-attachments/assets/de900117-0c6a-431d-ab62-c013834f97a9)


## Migration Guide

The `WorldQuery::Item` and `WorldQuery::Fetch` associated types and the
`QueryItem` and `ROQueryItem` type aliases now have an additional
lifetime parameter corresponding to the `'s` lifetime in `Query`. Manual
implementations of `WorldQuery` will need to update the method
signatures to include the new lifetimes. Other uses of the types will
need to be updated to include a lifetime parameter, although it can
usually be passed as `'_`. In particular, `ROQueryItem` is used when
implementing `RenderCommand`.

Before: 

```rust
fn render<'w>(
    item: &P,
    view: ROQueryItem<'w, Self::ViewQuery>,
    entity: Option<ROQueryItem<'w, Self::ItemQuery>>,
    param: SystemParamItem<'w, '_, Self::Param>,
    pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult;
```

After: 

```rust
fn render<'w>(
    item: &P,
    view: ROQueryItem<'w, '_, Self::ViewQuery>,
    entity: Option<ROQueryItem<'w, '_, Self::ItemQuery>>,
    param: SystemParamItem<'w, '_, Self::Param>,
    pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult;
```

---

Methods on `QueryState` that take `&mut self` may now result in
conflicting borrows if the query items capture the lifetime of the
mutable reference. This affects `get()`, `iter()`, and others. To fix
the errors, first call `QueryState::update_archetypes()`, and then
replace a call `state.foo(world, param)` with
`state.query_manual(world).foo_inner(param)`. Alternately, you may be
able to restructure the code to call `state.query(world)` once and then
make multiple calls using the `Query`.

Before:
```rust
let mut state: QueryState<_, _> = ...;
let d1 = state.get(world, e1);
let d2 = state.get(world, e2); // Error: cannot borrow `state` as mutable more than once at a time
println!("{d1:?}");
println!("{d2:?}");
```

After: 
```rust
let mut state: QueryState<_, _> = ...;

state.update_archetypes(world);
let d1 = state.get_manual(world, e1);
let d2 = state.get_manual(world, e2);
// OR
state.update_archetypes(world);
let d1 = state.query(world).get_inner(e1);
let d2 = state.query(world).get_inner(e2);
// OR
let query = state.query(world);
let d1 = query.get_inner(e1);
let d1 = query.get_inner(e2);

println!("{d1:?}");
println!("{d2:?}");
```
2025-06-16 21:05:41 +00:00
JMS55
e610badf6c
Add more PreviousViewData (#19605)
Add some more matrices to PreviousViewData for future use with
bevy_solari.
2025-06-16 04:54:26 +00:00
Alice Cecile
b7d2cb8547
Provide access to the original target of entity-events in observers (#19663)
# Objective

Getting access to the original target of an entity-event is really
helpful when working with bubbled / propagated events.

`bevy_picking` special-cases this, but users have requested this for all
sorts of bubbled events.

The existing naming convention was also very confusing. Fixes
https://github.com/bevyengine/bevy/issues/17112, but also see #18982.

## Solution

1. Rename `ObserverTrigger::target` -> `current_target`.
1. Store `original_target: Option<Entity>` in `ObserverTrigger`.
1. Wire it up so this field gets set correctly.
1. Remove the `target` field on the `Pointer` events from
`bevy_picking`.

Closes https://github.com/bevyengine/bevy/pull/18710, which attempted
the same thing. Thanks @emfax!

## Testing

I've modified an existing test to check that the entities returned
during event bubbling / propagation are correct.

## Notes to reviewers

It's a little weird / sad that you can no longer access this infromation
via the buffered events for `Pointer`. That said, you already couldn't
access any bubbled target. We should probably remove the `BufferedEvent`
form of `Pointer` to reduce confusion and overhead, but I didn't want to
do so here.

Observer events can be trivially converted into buffered events (write
an observer with an EventWriter), and I suspect that that is the better
migration if you want the controllable timing or performance
characteristics of buffered events for your specific use case.

## Future work

It would be nice to not store this data at all (and not expose any
methods) if propagation was disabled. That involves more trait
shuffling, and I don't think we should do it here for reviewability.

---------

Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
2025-06-15 20:53:25 +00:00
Alice Cecile
3926f0208f
Make sequential_dispersed fn constant (#19659)
# Objective

- Try to make more of `bevy_color` const now that we have
const_float_arithmetic.

## Solution

Fail abjectly, because of our heavy use of traits.

I did find these functions though, so you can have a PR 🙃
2025-06-15 16:56:07 +00:00
Joel Singh
12ad7479a0
Add insert_child and remove_child methods (#19622)
# Objective

- `remove_child` was mentioned missing in #19556 and I realized that
`insert_child` was also missing.
- Removes the need to wrap a single entity with `&[]` with
`remove_children` and `insert_children`
- Would have also added `despawn_children` but #19283 does so. 

## Solution

- Simple wrapper around `remove_related`

## Testing

- Added `insert_child` and `remove_child` tests analgous to
`insert_children` and `remove_children` and then ran `cargo run -p ci --
test`
2025-06-15 16:52:25 +00:00
theotherphil
510efdf905
deny(missing_docs) for bevy_mikktspace (#19654)
# Objective

Remove allow(missing_docs) from bevy_mikktspace and add a simple
top-level doc comment, towards
https://github.com/bevyengine/bevy/issues/3492
2025-06-15 16:50:23 +00:00
Rosy
83a1c07c01
lighting.rs example: Improved ambient light showcase (#19658)
# Objective

As someone who is currently learning Bevy, I found the implementation of
the ambient light in the 3d/lighting.rs example unsatisfactory.

## Solution

- I adjusted the brightness of the ambient light in the scene to 200
(where the default is 80). It was previously 0.02, a value so low it has
no noticeable effect.
- I added a keybind (space bar) to toggle the ambient light, allowing
users to see the difference it makes. I also added text showing the
state of the ambient light (on, off) and text showing the keybind.

I'm very new to Bevy and Rust, so apologies if any of this code is not
up to scratch.

## Testing

I checked all the text still updates correctly and all keybinds still
work. In my testing, it looks to work okay.
I'd appreciate others testing too, just to make sure. 

---

## Showcase

<details>
  <summary>Click to view showcase</summary>
<img width="960" alt="Screenshot (11)"
src="https://github.com/user-attachments/assets/916e569e-cd49-43fd-b81d-aae600890cd3"
/>
<img width="959" alt="Screenshot (12)"
src="https://github.com/user-attachments/assets/0e16bb3a-c38a-4a8d-8248-edf3b820d238"
/>
</details>
2025-06-15 16:49:48 +00:00
Joona Aalto
38c3423693
Event Split: Event, EntityEvent, and BufferedEvent (#19647)
# Objective

Closes #19564.

The current `Event` trait looks like this:

```rust
pub trait Event: Send + Sync + 'static {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
    
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```

The `Event` trait is used by both buffered events
(`EventReader`/`EventWriter`) and observer events. If they are observer
events, they can optionally be targeted at specific `Entity`s or
`ComponentId`s, and can even be propagated to other entities.

However, there has long been a desire to split the trait semantically
for a variety of reasons, see #14843, #14272, and #16031 for discussion.
Some reasons include:

- It's very uncommon to use a single event type as both a buffered event
and targeted observer event. They are used differently and tend to have
distinct semantics.
- A common footgun is using buffered events with observers or event
readers with observer events, as there is no type-level error that
prevents this kind of misuse.
- #19440 made `Trigger::target` return an `Option<Entity>`. This
*seriously* hurts ergonomics for the general case of entity observers,
as you need to `.unwrap()` each time. If we could statically determine
whether the event is expected to have an entity target, this would be
unnecessary.

There's really two main ways that we can categorize events: push vs.
pull (i.e. "observer event" vs. "buffered event") and global vs.
targeted:

|              | Push            | Pull                        |
| ------------ | --------------- | --------------------------- |
| **Global**   | Global observer | `EventReader`/`EventWriter` |
| **Targeted** | Entity observer | -                           |

There are many ways to approach this, each with their tradeoffs.
Ultimately, we kind of want to split events both ways:

- A type-level distinction between observer events and buffered events,
to prevent people from using the wrong kind of event in APIs
- A statically designated entity target for observer events to avoid
accidentally using untargeted events for targeted APIs

This PR achieves these goals by splitting event traits into `Event`,
`EntityEvent`, and `BufferedEvent`, with `Event` being the shared trait
implemented by all events.

## `Event`, `EntityEvent`, and `BufferedEvent`

`Event` is now a very simple trait shared by all events.

```rust
pub trait Event: Send + Sync + 'static {
    // Required for observer APIs
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```

You can call `trigger` for *any* event, and use a global observer for
listening to the event.

```rust
#[derive(Event)]
struct Speak {
    message: String,
}

// ...

app.add_observer(|trigger: On<Speak>| {
    println!("{}", trigger.message);
});

// ...

commands.trigger(Speak {
    message: "Y'all like these reworked events?".to_string(),
});
```

To allow an event to be targeted at entities and even propagated
further, you can additionally implement the `EntityEvent` trait:

```rust
pub trait EntityEvent: Event {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
}
```

This lets you call `trigger_targets`, and to use targeted observer APIs
like `EntityCommands::observe`:

```rust
#[derive(Event, EntityEvent)]
#[entity_event(traversal = &'static ChildOf, auto_propagate)]
struct Damage {
    amount: f32,
}

// ...

let enemy = commands.spawn((Enemy, Health(100.0))).id();

// Spawn some armor as a child of the enemy entity.
// When the armor takes damage, it will bubble the event up to the enemy.
let armor_piece = commands
    .spawn((ArmorPiece, Health(25.0), ChildOf(enemy)))
    .observe(|trigger: On<Damage>, mut query: Query<&mut Health>| {
        // Note: `On::target` only exists because this is an `EntityEvent`.
        let mut health = query.get(trigger.target()).unwrap();
        health.0 -= trigger.amount();
    });

commands.trigger_targets(Damage { amount: 10.0 }, armor_piece);
```

> [!NOTE]
> You *can* still also trigger an `EntityEvent` without targets using
`trigger`. We probably *could* make this an either-or thing, but I'm not
sure that's actually desirable.

To allow an event to be used with the buffered API, you can implement
`BufferedEvent`:

```rust
pub trait BufferedEvent: Event {}
```

The event can then be used with `EventReader`/`EventWriter`:

```rust
#[derive(Event, BufferedEvent)]
struct Message(String);

fn write_hello(mut writer: EventWriter<Message>) {
    writer.write(Message("I hope these examples are alright".to_string()));
}

fn read_messages(mut reader: EventReader<Message>) {
    // Process all buffered events of type `Message`.
    for Message(message) in reader.read() {
        println!("{message}");
    }
}
```

In summary:

- Need a basic event you can trigger and observe? Derive `Event`!
- Need the event to be targeted at an entity? Derive `EntityEvent`!
- Need the event to be buffered and support the
`EventReader`/`EventWriter` API? Derive `BufferedEvent`!

## Alternatives

I'll now cover some of the alternative approaches I have considered and
briefly explored. I made this section collapsible since it ended up
being quite long :P

<details>

<summary>Expand this to see alternatives</summary>

### 1. Unified `Event` Trait

One option is not to have *three* separate traits (`Event`,
`EntityEvent`, `BufferedEvent`), and to instead just use associated
constants on `Event` to determine whether an event supports targeting
and buffering or not:

```rust
pub trait Event: Send + Sync + 'static {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
    const TARGETED: bool = false;
    const BUFFERED: bool = false;
    
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```

Methods can then use bounds like `where E: Event<TARGETED = true>` or
`where E: Event<BUFFERED = true>` to limit APIs to specific kinds of
events.

This would keep everything under one `Event` trait, but I don't think
it's necessarily a good idea. It makes APIs harder to read, and docs
can't easily refer to specific types of events. You can also create
weird invariants: what if you specify `TARGETED = false`, but have
`Traversal` and/or `AUTO_PROPAGATE` enabled?

### 2. `Event` and `Trigger`

Another option is to only split the traits between buffered events and
observer events, since that is the main thing people have been asking
for, and they have the largest API difference.

If we did this, I think we would need to make the terms *clearly*
separate. We can't really use `Event` and `BufferedEvent` as the names,
since it would be strange that `BufferedEvent` doesn't implement
`Event`. Something like `ObserverEvent` and `BufferedEvent` could work,
but it'd be more verbose.

For this approach, I would instead keep `Event` for the current
`EventReader`/`EventWriter` API, and call the observer event a
`Trigger`, since the "trigger" terminology is already used in the
observer context within Bevy (both as a noun and a verb). This is also
what a long [bikeshed on
Discord](https://discord.com/channels/691052431525675048/749335865876021248/1298057661878898791)
seemed to land on at the end of last year.

```rust
// For `EventReader`/`EventWriter`
pub trait Event: Send + Sync + 'static {}

// For observers
pub trait Trigger: Send + Sync + 'static {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
    const TARGETED: bool = false;
    
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}
```

The problem is that "event" is just a really good term for something
that "happens". Observers are rapidly becoming the more prominent API,
so it'd be weird to give them the `Trigger` name and leave the good
`Event` name for the less common API.

So, even though a split like this seems neat on the surface, I think it
ultimately wouldn't really work. We want to keep the `Event` name for
observer events, and there is no good alternative for the buffered
variant. (`Message` was suggested, but saying stuff like "sends a
collision message" is weird.)

### 3. `GlobalEvent` + `TargetedEvent`

What if instead of focusing on the buffered vs. observed split, we
*only* make a distinction between global and targeted events?

```rust
// A shared event trait to allow global observers to work
pub trait Event: Send + Sync + 'static {
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}

// For buffered events and non-targeted observer events
pub trait GlobalEvent: Event {}

// For targeted observer events
pub trait TargetedEvent: Event {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
}
```

This is actually the first approach I implemented, and it has the neat
characteristic that you can only use non-targeted APIs like `trigger`
with a `GlobalEvent` and targeted APIs like `trigger_targets` with a
`TargetedEvent`. You have full control over whether the entity should or
should not have a target, as they are fully distinct at the type-level.

However, there's a few problems:

- There is no type-level indication of whether a `GlobalEvent` supports
buffered events or just non-targeted observer events
- An `Event` on its own does literally nothing, it's just a shared trait
required to make global observers accept both non-targeted and targeted
events
- If an event is both a `GlobalEvent` and `TargetedEvent`, global
observers again have ambiguity on whether an event has a target or not,
undermining some of the benefits
- The names are not ideal

### 4. `Event` and `EntityEvent`

We can fix some of the problems of Alternative 3 by accepting that
targeted events can also be used in non-targeted contexts, and simply
having the `Event` and `EntityEvent` traits:

```rust
// For buffered events and non-targeted observer events
pub trait Event: Send + Sync + 'static {
    fn register_component_id(world: &mut World) -> ComponentId { ... }
    fn component_id(world: &World) -> Option<ComponentId> { ... }
}

// For targeted observer events
pub trait EntityEvent: Event {
    type Traversal: Traversal<Self>;
    const AUTO_PROPAGATE: bool = false;
}
```

This is essentially identical to this PR, just without a dedicated
`BufferedEvent`. The remaining major "problem" is that there is still
zero type-level indication of whether an `Event` event *actually*
supports the buffered API. This leads us to the solution proposed in
this PR, using `Event`, `EntityEvent`, and `BufferedEvent`.

</details>

## Conclusion

The `Event` + `EntityEvent` + `BufferedEvent` split proposed in this PR
aims to solve all the common problems with Bevy's current event model
while keeping the "weirdness" factor minimal. It splits in terms of both
the push vs. pull *and* global vs. targeted aspects, while maintaining a
shared concept for an "event".

### Why I Like This

- The term "event" remains as a single concept for all the different
kinds of events in Bevy.
- Despite all event types being "events", they use fundamentally
different APIs. Instead of assuming that you can use an event type with
any pattern (when only one is typically supported), you explicitly opt
in to each one with dedicated traits.
- Using separate traits for each type of event helps with documentation
and clearer function signatures.
- I can safely make assumptions on expected usage.
- If I see that an event is an `EntityEvent`, I can assume that I can
use `observe` on it and get targeted events.
- If I see that an event is a `BufferedEvent`, I can assume that I can
use `EventReader` to read events.
- If I see both `EntityEvent` and `BufferedEvent`, I can assume that
both APIs are supported.

In summary: This allows for a unified concept for events, while limiting
the different ways to use them with opt-in traits. No more guess-work
involved when using APIs.

### Problems?

- Because `BufferedEvent` implements `Event` (for more consistent
semantics etc.), you can still use all buffered events for non-targeted
observers. I think this is fine/good. The important part is that if you
see that an event implements `BufferedEvent`, you know that the
`EventReader`/`EventWriter` API should be supported. Whether it *also*
supports other APIs is secondary.
- I currently only support `trigger_targets` for an `EntityEvent`.
However, you can technically target components too, without targeting
any entities. I consider that such a niche and advanced use case that
it's not a huge problem to only support it for `EntityEvent`s, but we
could also split `trigger_targets` into `trigger_entities` and
`trigger_components` if we wanted to (or implement components as
entities :P).
- You can still trigger an `EntityEvent` *without* targets. I consider
this correct, since `Event` implements the non-targeted behavior, and
it'd be weird if implementing another trait *removed* behavior. However,
it does mean that global observers for entity events can technically
return `Entity::PLACEHOLDER` again (since I got rid of the
`Option<Entity>` added in #19440 for ergonomics). I think that's enough
of an edge case that it's not a huge problem, but it is worth keeping in
mind.
- ~~Deriving both `EntityEvent` and `BufferedEvent` for the same type
currently duplicates the `Event` implementation, so you instead need to
manually implement one of them.~~ Changed to always requiring `Event` to
be derived.

## Related Work

There are plans to implement multi-event support for observers,
especially for UI contexts. [Cart's
example](https://github.com/bevyengine/bevy/issues/14649#issuecomment-2960402508)
API looked like this:

```rust
// Truncated for brevity
trigger: Trigger<(
    OnAdd<Pressed>,
    OnRemove<Pressed>,
    OnAdd<InteractionDisabled>,
    OnRemove<InteractionDisabled>,
    OnInsert<Hovered>,
)>,
```

I believe this shouldn't be in conflict with this PR. If anything, this
PR might *help* achieve the multi-event pattern for entity observers
with fewer footguns: by statically enforcing that all of these events
are `EntityEvent`s in the context of `EntityCommands::observe`, we can
avoid misuse or weird cases where *some* events inside the trigger are
targeted while others are not.
2025-06-15 16:46:34 +00:00
andriyDev
98c14e5917
Replace UntypedHandle from ReflectAsset with impl Into<UntypedAssetId>. (#19606)
# Objective

- A step towards #19024.
- Allow `ReflectAsset` to work with any `AssetId` not just `Handle`.
- `ReflectAsset::ids()` returns an iterator of `AssetId`s, but then
there's no way to use these ids, since all the other APIs in
`ReflectAsset` require a handle (and we don't have a reflect way to get
the handle).

## Solution

- Replace the `UntypedHandle` argument in `ReflectAsset` methods with
`impl Into<UntypedAssetId>`.
- This matches the regular asset API.
- This allows `ReflectAsset::ids()` to be more useful.

## Testing

- None.
2025-06-15 16:42:54 +00:00
urben1680
8bce3e46f2
Preserve extra data in RelationshipTarget with replace_related(_with_difference) (#19588)
# Objective

The methods and commands `replace_related` and
`replace_related_with_difference` may cause data stored at the
`RelationshipTarget` be lost when all original children are removed
before new children are added.

Part of https://github.com/bevyengine/bevy/issues/19589

## Solution

Fix the issue, either by removing the old children _after_ adding the
new ones and not _before_ (`replace_related_with_difference`) or by
taking the whole `RelationshipTarget` to modify it, not only the inner
collection (`replace_related`).

## Testing

I added a new test asserting the data is kept. I also added a general
test of these methods as they had none previously.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-15 16:41:28 +00:00
JMS55
6ecaf9de53
Add SkipDeferredLighting component (#19628)
Adds a new component for when you want to run the deferred gbuffer
prepass, but not the lighting pass.

This will be used by bevy_solari in the future, as it'll do it's own
shading pass, but still wants the gbuffer.
2025-06-15 16:30:08 +00:00
Trent
29cfad63a1
Fix typos (#19651)
Minor typo fix
2025-06-15 07:49:54 +00:00
Talin
30aa36eaf4
Core slider (#19584)
# Objective

This is part of the "core widgets" effort: #19236. 

## Solution

This PR adds the "core slider" widget to the collection.

## Testing

Tested using examples `core_widgets` and `core_widgets_observers`.

---------

Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
2025-06-15 00:53:31 +00:00
andriyDev
f47b1c00ee
Bump ron version to 0.10. (#19631)
# Objective

- Update ron to the latest version.
- This is blocking changes to AnimationGraph (as some valid structs are
not capable of being deserialized).

## Solution

- Bump ron!

## Testing

- The particular issue I was blocked by seems to be resolved!
2025-06-13 19:54:31 +00:00
Design_Dream
2b0a05cbb8
Fix incorrect description of ClusteredDecal (#19630)
The documentation states that ClusteredDecal projects in the +Z
direction, but in practice, it projects in the -Z direction, which can
be confusing.

# Objective

Fixes #19612
2025-06-13 19:31:09 +00:00
urben1680
a292ac539e
System::check_change_tick and similar methods take CheckChangeTicks (#19600)
Follow-up of #19274.

Make the `check_change_tick` methods, of which some are now public, take
`CheckChangeTicks` to make it obvious where this tick comes from, see
other PR.

This also affects the `System` trait, hence the many changed files.

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-06-13 19:24:27 +00:00
Chris Russell
bb4ea9c28b
Stop storing access for all systems (#19477)
# Objective

Reduce memory usage by storing fewer copies of
`FilteredAccessSet<ComponentId>`.

Currently, the `System` trait exposes the `component_access_set` for the
system, which is used by the multi-threaded executor to determine which
systems can run concurrently. But because it is available on the trait,
it needs to be stored for *every* system, even ones that are not run by
the executor! In particular, it is never needed for observers, or for
the inner systems in a `PipeSystem` or `CombinatorSystem`.


## Solution

Instead of exposing the access from a method on `System`, return it from
`System::initialize`. Since it is still needed during scheduling, store
the access alongside the boxed system in the schedule.

That's not quite enough for systems built using `SystemParamBuilder`s,
though. Those calculate the access in `SystemParamBuilder::build`, which
happens earlier than `System::initialize`. To handle those, we separate
`SystemParam::init_state` into `init_state`, which creates the state
value, and `init_access`, which calculates the access. This lets
`System::initialize` call `init_access` on a state that was provided by
the builder.

An additional benefit of that separation is that it removes the need to
duplicate access checks between `SystemParamBuilder::build` and
`SystemParam::init_state`.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-13 17:56:09 +00:00
Erick Z
4da420cc4c
Fixing AutoFocus not working if it's spawn before Startup (#19618)
# Objective

- `AutoFocus` component does seem to work with the `set_initial_focus`
that was running in `Startup`.
- If an element is spawn during `PreStartup` or during
`OnEnter(SomeState)` that happens before `Startup`, the focus is
overridden by `set_initial_focus` which sets the focus to the primary
window.


## Solution

- `set_initial_focus` only sets the focus to the `PrimaryWindow` if no
other focus is set.

- *Note*: `cargo test --package bevy_input_focus` was not working, so
some changes are related to that.

## Testing

- `cargo test --package bevy_input_focus`: OK
- `cargo run --package ci`: OK
2025-06-13 17:15:47 +00:00
Kevin Reid
ce3db843bc
Make IrradianceVolume require LightProbe (and document this). (#19621)
## Objective

Make it easier to use `IrradianceVolume` with fewer ways to silently
fail. Fix #19614.

## Solution

* Add `#[require(LightProbe)]` to `struct IrradianceVolume`.
* Document this fact.
* Also document the volume being centered on the origin by default (this
was the other thing that was unclear when getting started).

I also looked at the other implementor of `LightProbeComponent`,
`EnvironmentMapLight`, but it has a use which is *not* as a light probe,
so it should not require `LightProbe`.

## Testing

* Confirmed that `examples/3d/irradiance_volumes.rs` still works after
removing `LightProbe`.
* Reviewed generated documentation.
2025-06-13 17:09:31 +00:00
Erick Z
8203e5c4e0
Fixing running ci locally in MacOS (#19619)
# Objective

- Running `cargo run --package ci` in MacOS does not currently work in
`main`.
- It shows a `error: this lint expectation is unfulfilled`.
- Fixes #19583

## Solution

- Remove an unnecessary `#[expect(clippy::large_enum_variant)]` on a
function.

## Testing

- `cargo run --package ci`: 👍
2025-06-13 14:51:28 +00:00
Al M.
ef71186d44
Fix LoadContext docs. (#19610)
"add_labeled_asset" (add `Asset`) should be "add_loaded_labeled_asset"
(add `LoadedAsset`).
2025-06-13 03:48:55 +00:00
Joel Singh
fa449e6267
Add example to Single docs (#19461) (#19603)
# Objective

- Add example to `Single` docs, highlighting that you can use methods
and properties directly.
- Fixes #19461

## Solution

- Added example to inline docs of `Single`

## Testing

- `cargo test --doc`
- `cargo doc --open`

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-13 00:46:11 +00:00
François Mockers
2961f44dcd
don't deny warnings on wasm-atomics CI job (#19604)
# Objective

- CI job checking for wasm atomics fails because of an unrelated lint on
nightly rust
- Fixes #19573 

## Solution

- Don't deny warning, that's not what this job is checking anyway
2025-06-12 23:29:50 +00:00
JMS55
bab31e3777
Initial raytraced lighting progress (bevy_solari) (#19058)
# Bevy Solari 
<img
src="https://github.com/user-attachments/assets/94061fc8-01cf-4208-b72a-8eecad610d76"
width="100" />

## Preface
- See release notes.
- Please talk to me in #rendering-dev on discord or open a github
discussion if you have questions about the long term plan, and keep
discussion in this PR limited to the contents of the PR :)

## Connections
- Works towards #639, #16408.
- Spawned https://github.com/bevyengine/bevy/issues/18993.
- Need to fix RT stuff in naga_oil first
https://github.com/bevyengine/naga_oil/pull/116.

## This PR

After nearly two years, I've revived the raytraced lighting effort I
first started in https://github.com/bevyengine/bevy/pull/10000.

Unlike that PR, which has realtime techniques, I've limited this PR to:
* `RaytracingScenePlugin` - BLAS and TLAS building, geometry and texture
binding, sampling functions.
* `PathtracingPlugin` - A non-realtime path tracer intended to serve as
a testbed and reference.

## What's implemented?

![image](https://github.com/user-attachments/assets/06522007-c205-46eb-8178-823f19917def)

* BLAS building on mesh load
* Emissive lights
* Directional lights with soft shadows
* Diffuse (lambert, not Bevy's diffuse BRDF) and emissive materials
* A reference path tracer with:
  * Antialiasing
  * Direct light sampling (next event estimation) with 0/1 MIS weights
  * Importance-sampled BRDF bounces
  * Russian roulette 

## What's _not_ implemented?
* Anything realtime, including a real-time denoiser
* Integration with Bevy's rasterized gbuffer
* Specular materials
* Non-opaque geometry
* Any sort of CPU or GPU optimizations
* BLAS compaction, proper bindless, and further RT APIs are things that
we need wgpu to add
* PointLights, SpotLights, or skyboxes / environment lighting 
* Support for materials other than StandardMaterial (and only a subset
of properties are supported)
* Skinned/morphed or otherwise animating/deformed meshes
* Mipmaps
* Adaptive self-intersection ray bias
* A good way for developers to detect whether the user's GPU supports RT
or not, and fallback to baked lighting.
* Documentation and actual finalized APIs (literally everything is
subject to change)

## End-user Usage
* Have a GPU that supports RT with inline ray queries
* Add `SolariPlugin` to your app
* Ensure any `Mesh` asset you want to use for raytracing has
`enable_raytracing: true` (defaults to true), and that it uses the
standard uncompressed position/normal/uv_0/tangent vertex attribute set,
triangle list topology, and 32-bit indices.
* If you don't want to build a BLAS and use the mesh for RT, set
enable_raytracing to false.
* Add the `RaytracingMesh3d` component to your entity (separate from
`Mesh3d` or `MeshletMesh3d`).

## Testing

- Did you test these changes? If so, how? 
  - Ran the solari example.
- Are there any parts that need more testing?
  - Other test scenes probably. Normal mapping would be good to test.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - See the solari.rs example for how to setup raytracing.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - Windows 11, NVIDIA RTX 3080.

---------

Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2025-06-12 21:26:10 +00:00
Alice Cecile
58c276ab44
Make the ObservedBy component useful to public consumers (#19591)
# Objective

As raised by @Jondolf, this type is `pub`, and useful for various
consumers to ensure cleanup or debugging.

However, it doesn't offer any way to actually view the data.

## Solution

- Add a read-only view of the data.
- Don't add any (easy) way to mutate the data, as this presents a huge
footgun.
- Implement Reflect and register the component so you can see it in
inspectors nicely.
2025-06-12 20:07:11 +00:00
Kevin Reid
1dfe83bb8d
Fix headless_renderer example and mention Screenshot. (#19598)
## Objective

- Makes `headless_renderer` example work instead of exiting without
effect.
- Guides users who actually just need
[`Screenshot`](https://docs.rs/bevy/0.16.1/bevy/render/view/window/screenshot/struct.Screenshot.html)
to use that instead.

This PR was inspired by my own efforts to do headless rendering, in
which the complexity of the `headless_renderer` example was a
distraction, and this comment from
https://github.com/bevyengine/bevy/issues/12478#issuecomment-2094925039
:

> The example added in https://github.com/bevyengine/bevy/pull/13006
would benefit from this change: be sure to clean it up when tackling
this work :)

That “cleanup” was not done, and I thought to do it, but it seems to me
that using `Screenshot` (in its current form) in the example would not
be correct, because — if I understand correctly — the example is trying
to, potentially, capture many *consecutive* frames, whereas `Screenshot`
by itself gives no means to capture multiple frames without gaps or
duplicates. But perhaps I am wrong (the code is complex and not clearly
documented), or perhaps that feature isn’t worth preserving. In that
case, let me know and I will revise this PR.

## Solution

- Added `exit_condition: bevy:🪟:ExitCondition::DontExit`
- Added a link to `Screenshot` in the crate documentation.

## Testing

- Ran the example and confirmed that it now writes an image file and
then exits.
2025-06-12 20:06:08 +00:00
Joshua Holmes
8010185275
Make GILRS and WINIT_WINDOWS public (#19575)
After removing `!Send` resources, `GILRS` and `WINIT_WINDOWS` were not
made public, which is a breaking change. This was brought up in a
[comment on that
PR](https://github.com/bevyengine/bevy/pull/18386#issuecomment-2954209010).
This PR makes them public.

Fixes #19540.
2025-06-12 20:05:00 +00:00
Joona Aalto
e5dc177b4b
Rename Trigger to On (#19596)
# Objective

Currently, the observer API looks like this:

```rust
app.add_observer(|trigger: Trigger<Explode>| {
    info!("Entity {} exploded!", trigger.target());
});
```

Future plans for observers also include "multi-event observers" with a
trigger that looks like this (see [Cart's
example](https://github.com/bevyengine/bevy/issues/14649#issuecomment-2960402508)):

```rust
trigger: Trigger<(
    OnAdd<Pressed>,
    OnRemove<Pressed>,
    OnAdd<InteractionDisabled>,
    OnRemove<InteractionDisabled>,
    OnInsert<Hovered>,
)>,
```

In scenarios like this, there is a lot of repetition of `On`. These are
expected to be very high-traffic APIs especially in UI contexts, so
ergonomics and readability are critical.

By renaming `Trigger` to `On`, we can make these APIs read more cleanly
and get rid of the repetition:

```rust
app.add_observer(|trigger: On<Explode>| {
    info!("Entity {} exploded!", trigger.target());
});
```

```rust
trigger: On<(
    Add<Pressed>,
    Remove<Pressed>,
    Add<InteractionDisabled>,
    Remove<InteractionDisabled>,
    Insert<Hovered>,
)>,
```

Names like `On<Add<Pressed>>` emphasize the actual event listener nature
more than `Trigger<OnAdd<Pressed>>`, and look cleaner. This *also* frees
up the `Trigger` name if we want to use it for the observer event type,
splitting them out from buffered events (bikeshedding this is out of
scope for this PR though).

For prior art:
[`bevy_eventlistener`](https://github.com/aevyrie/bevy_eventlistener)
used
[`On`](https://docs.rs/bevy_eventlistener/latest/bevy_eventlistener/event_listener/struct.On.html)
for its event listener type. Though in our case, the observer is the
event listener, and `On` is just a type containing information about the
triggered event.

## Solution

Steal from `bevy_event_listener` by @aevyrie and use `On`.

- Rename `Trigger` to `On`
- Rename `OnAdd` to `Add`
- Rename `OnInsert` to `Insert`
- Rename `OnReplace` to `Replace`
- Rename `OnRemove` to `Remove`
- Rename `OnDespawn` to `Despawn`

## Discussion

### Naming Conflicts??

Using a name like `Add` might initially feel like a very bad idea, since
it risks conflict with `core::ops::Add`. However, I don't expect this to
be a big problem in practice.

- You rarely need to actually implement the `Add` trait, especially in
modules that would use the Bevy ECS.
- In the rare cases where you *do* get a conflict, it is very easy to
fix by just disambiguating, for example using `ops::Add`.
- The `Add` event is a struct while the `Add` trait is a trait (duh), so
the compiler error should be very obvious.

For the record, renaming `OnAdd` to `Add`, I got exactly *zero* errors
or conflicts within Bevy itself. But this is of course not entirely
representative of actual projects *using* Bevy.

You might then wonder, why not use `Added`? This would conflict with the
`Added` query filter, so it wouldn't work. Additionally, the current
naming convention for observer events does not use past tense.

### Documentation

This does make documentation slightly more awkward when referring to
`On` or its methods. Previous docs often referred to `Trigger::target`
or "sends a `Trigger`" (which is... a bit strange anyway), which would
now be `On::target` and "sends an observer `Event`".

You can see the diff in this PR to see some of the effects. I think it
should be fine though, we may just need to reword more documentation to
read better.
2025-06-12 18:22:33 +00:00
LP
8ab71a6999
Modified the "scroll.rs" example to use the new spawning API. (#19592)
# Objective

- Update the scroll example to use the latest API.

## Solution

- It now uses the 'children![]' API.

## Testing

- I manually verified that the scrolling was working

## Limitations
- Unfortunately, I couldn't find a way to spawn observers targeting the
entity inside the "fn() -> impl Bundle" function.
2025-06-12 02:32:18 +00:00
Al M.
0d620cdf29
Update example doc link for ImageLoaderSettings. (#19565)
Link in the "asset settings" example. The struct was moved.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
2025-06-11 22:43:40 +00:00
张林伟
98a46f79df
Fix bevy_core_widgets crate description typo (#19578)
# Objective

Fix `bevy_core_widgets`  crate description typo.
2025-06-11 22:30:35 +00:00
Alice Cecile
12f8f9c07c
Thoroughly document the current state of observers (#19590)
# Objective

The documentation for observers is not very good. This poses a problem
to users, but *also* causes serious problems for engine devs, as they
attempt to improve assorted issues surrounding observers.

This PR:

- Fixes #14084.
- Fixes #14726.
- Fixes #16538.
- Closes #18914, by attempting to solve the same issue.

To keep this PR at all reviewable, I've opted to simply note the various
limitations (some may call them bugs!) in place, rather than attempting
to fix them. There is a huge amount of cleanup work to be done here: see
https://github.com/orgs/bevyengine/projects/17.

## Solution

- Write good module docs for observers, offering bread crumbs to the
most common methods and techniques and comparing-and-contrasting as
needed.
- Fix any actively misleading documentation.
- Try to explain how the various bits of the (public?!) internals are
related.

---------

Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
2025-06-11 22:04:52 +00:00
Joona Aalto
33c6f45a35
Rename some pointer events and components (#19574)
# Objective

#19366 implemented core button widgets, which included the `Depressed`
state component.

`Depressed` was chosen instead of `Pressed` to avoid conflict with the
`Pointer<Pressed>` event, but it is problematic and awkward in many
ways:

- Using the word "depressed" for such a high-traffic type is not great
due to the obvious connection to "depressed" as in depression.
- "Depressed" is not what I would search for if I was looking for a
component like this, and I'm not aware of any other engine or UI
framework using the term.
- `Depressed` is not a very natural pair to the `Pointer<Pressed>`
event.
- It might be because I'm not a native English speaker, but I have very
rarely heard someone say "a button is depressed". Seeing it, my mind
initially goes from "depression??" to "oh, de-pressed, meaning released"
and definitely not "is pressed", even though that *is* also a valid
meaning for it.

A related problem is that the current `Pointer<Pressed>` and
`Pointer<Released>` event names use a different verb tense than all of
our other observer events such as `Pointer<Click>` or
`Pointer<DragStart>`. By fixing this and renaming `Pressed` (and
`Released`), we can then use `Pressed` instead of `Depressed` for the
state component.

Additionally, the `IsHovered` and `IsDirectlyHovered` components added
in #19366 use an inconsistent naming; the other similar components don't
use an `Is` prefix. It also makes query filters like `Has<IsHovered>`
and `With<IsHovered>` a bit more awkward.

This is partially related to Cart's [picking concept
proposal](https://gist.github.com/cart/756e48a149db2838028be600defbd24a?permalink_comment_id=5598154).

## Solution

- Rename `Pointer<Pressed>` to `Pointer<Press>`
- Rename `Pointer<Released>` to `Pointer<Release>`
- Rename `Depressed` to `Pressed`
- Rename `IsHovered` to `Hovered`
- Rename `IsDirectlyHovered` to `DirectlyHovered`
2025-06-10 21:57:28 +00:00
Mads Marquart
27b64c3bf1
Fix iOS simulator build (#19498)
# Objective

Fixes https://github.com/bevyengine/bevy/issues/18893.

## Solution

`bindgen` currently has issues with building Xcode 16.3 and above, due
to Clang being more strict about target triples. It was fixed in
https://github.com/rust-lang/rust-bindgen/pull/3182, but as a breaking
release, so affected dependencies have to be updated to either not
depend on `bindgen`, or depend on a fixed version of it:
- `tracing-oslog`: https://github.com/Absolucy/tracing-oslog/pull/12
- `coreaudio-sys`: https://github.com/RustAudio/coreaudio-sys/pull/114

## Testing

```sh
cargo build --target aarch64-apple-ios-sim
```

With Xcode 16.3 or 16.4.
2025-06-10 17:01:37 +00:00
Rob Parrett
8537718c6b
Fix pbr example text rotation (#19571)
# Objective

This example migration was missed in #16615


https://pixel-eagle.com/project/b25a040a-a980-4602-b90c-d480ab84076d/run/10633/compare/10627?screenshot=3D+Rendering/pbr.png

## Solution

Use new `UiTransform`

## Testing

`cargo run --example pbr`
2025-06-10 16:57:57 +00:00
Talin
57ddae1e93
Core button widget (#19366)
# Objective

Part of #19236

## Solution

Adds a new `bevy_core_widgets` crate containing headless widget
implementations. This PR adds a single `CoreButton` widget, more widgets
to be added later once this is approved.

## Testing

There's an example, ui/core_widgets.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-10 12:50:08 -04:00
Tero Laxström
c549b9e9a4
Copy stress test settings for many_camera_lights (#19512)
# Objective

- Fixes #17183

## Solution

- Copied the stress settings from the `many_animated_sprite` example
that were mentioned in the ticket

## Testing

- Run the example

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-10 01:12:55 +00:00
Alice Cecile
030edbf3fe
Rename bevy_ecs::world::Entry to ComponentEntry (#19517)
# Objective

As discussed in #19285, some of our names conflict. `Entry` in bevy_ecs
is one of those overly general names.

## Solution

Rename this type (and the related types) to `ComponentEntry`.

---------

Co-authored-by: urben1680 <55257931+urben1680@users.noreply.github.com>
2025-06-10 01:12:40 +00:00
Alice Cecile
6ddd0f16a8
Component lifecycle reorganization and documentation (#19543)
# Objective

I set out with one simple goal: clearly document the differences between
each of the component lifecycle events via module docs.

Unfortunately, no such module existed: the various lifecycle code was
scattered to the wind.
Without a unified module, it's very hard to discover the related types,
and there's nowhere good to put my shiny new documentation.

## Solution

1. Unify the assorted types into a single
`bevy_ecs::component_lifecycle` module.
2. Write docs.
3. Write a migration guide.

## Testing

Thanks CI!

## Follow-up

1. The lifecycle event names are pretty confusing, especially
`OnReplace`. We should consider renaming those. No bikeshedding in my PR
though!
2. Observers need real module docs too :(
3. Any additional functional changes should be done elsewhere; this is a
simple docs and re-org PR.

---------

Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
2025-06-10 00:59:16 +00:00
Ayan Das
c35df3ca66
Added async_executor to the array of features enabled by multi_threaded within bevy_tasks crate to prevent compile-time error when default-features are disabled. (#19334)
## Objective

Fixes #19051.

---

## Solution
Originally implemented `compiler_error!()` within bevy_tasks/src/lib.rs
file to provide descriptive message regarding missing feature. However,
a cleaner approach was to add `async_executor` to the array of features
enabled by `multi_threaded` instead. This removes the need for users to
manually add `features = ["multi_threaded", "async_executor"]`
separately as needed to be done previously.

---

## Testing

These changes were tested using a minimal, external project designed to
specifically test whether the standalone `multi_threaded` feature for
`bevy_tasks` with `default-features` disabled worked as intended without
running into any compile-time error.

### How it was tested:
1. A `bevy_tasks_test` binary project was set up in an external
directory.
2. Its `Cargo.toml` was configured to depend on the local `bevy_tasks`,
explicitly disabling default features and enabling only
`multi_threaded`.
3. A simple `bevy_tasks_test/bin/bevy_crates_test.rs` was created and
configured within `Cargo.toml` file where bevy_tasks was set to the
local version of the crate with the modified changes and `cargo add
bevy_platform` was executed through the terminal since that dependency
is also needed to execute the sample examples.
4. Then both the `examples/busy_behavior.rs` and
`examples/idle_behavior.rs` code was added and tested with just the
`multi_threaded` feature was enabled and the code executed successfully.

### Results:
The code executed successfully for both examples where a threadPool was
utilized with 4 tasks and spawning 40 tasks that spin for 100ms.
Demonstrating how the threads finished executing all the tasks
simultaneously after a brief delay of less than a second (this is
referencing `bevy_tasks/examples/busy_behavior.rs`). Alongside the
second example where one thread per logical core was was utilized for a
single spinning task and aside from utilizing the single thread, system
was intended to remain idle as part of good practice when it comes to
handling small workloads. (this is referencing
`bevy_tasks/examples/idle_behavior.rs`).

### How to test:
Reviewers can easily verify this by:
1.  Checking out this PR.
2. Creating `cargo new bevy_tasks_test` and the `Cargo.toml` should look
something like this:
    ```toml
    # bevy/tests/compile_fail_tasks/Cargo.toml
    [package]
    name = "bevy_tasks_test"
    version = "0.1.0"
    edition = "2021"
    publish = false

    [dependencies]
    # path to bevy_tasks sub-crate to test locally
bevy_tasks = { path = "../../bevy_tasks", default-features = false,
features = ["multi_threaded"] }
    bevy_platform = "0.16.1"
    ```
3. Copying the examples within bevy_creates/examples one by one and
testing them separately within `src/main.rs` to check whether the
examples work.
4. Then simply running `cargo run` within the terminal should suffice to
test if the single `multi_threaded` feature works without the need for
separately adding `async_executor` as `multi_threaded` already uses
`async_executor` internally.

### Platforms tested:
macOS (aarch64). As a `#[cfg]` based compile-time check, behavior should
be consistent across platforms.
2025-06-10 00:54:46 +00:00
Guillaume Wafo-Tapa
0e805eb49c
Implement SystemCondition for systems returning Result<bool, BevyError> and Result<(), BevyError> (#19553)
# Objective

Fixes #19403
As described in the issue, the objective is to support the use of
systems returning `Result<(), BevyError>` and
`Result<bool, BevyError>` as run conditions. In these cases, the run
condition would hold on `Ok(())` and `Ok(true)` respectively.


## Solution

`IntoSystem<In, bool, M>` cannot be implemented for systems returning
`Result<(), BevyError>` and `Result<bool, BevyError>` as that would
conflict with their trivial implementation of the trait. That led me to
add a method to the sealed trait `SystemCondition` that does the
conversion. In the original case of a system returning `bool`, the
system is returned as is. With the new types, the system is combined
with `map()` to obtain a `bool`.

By the way, I'm confused as to why `SystemCondition` has a generic `In`
parameter as it is only ever used with `In = ()` as far as I can tell.

## Testing

I added a simple test for both type of system. That's minimal but it
felt enough. I could not picture the more complicated tests passing for
a run condition returning `bool` and failing for the new types.

## Doc

I documenting the change on the page of the trait. I had trouble wording
it right but I'm not sure how to improve it. The phrasing "the condition
returns `true`" which reads naturally is now technically incorrect as
the new types return a `Result`. However, the underlying condition
system that the implementing system turns into does indeed return
`bool`. But talking about the implementation details felt too much.
Another possibility is to use another turn of phrase like "the condition
holds" or "the condition checks out". I've left "the condition returns
`true`" in the documentation of `run_if` and the provided methods for
now.

I'm perplexed about the examples. In the first one, why not implement
the condition directly instead of having a system returning it? Is it
from a time of Bevy where you had to implement your conditions that way?
In that case maybe that should be updated. And in the second example I'm
missing the point entirely. As I stated above, I've only seen conditions
used in contexts where they have no input parameter. Here we create a
condition with an input parameter (cannot be used by `run_if`) and we
are using it with `pipe()` which actually doesn't need our system to
implement `SystemCondition`. Both examples are also calling
`IntoSystem::into_system` which should not be encouraged. What am I
missing?
2025-06-10 00:04:10 +00:00
dependabot[bot]
06bb57c326
Bump crate-ci/typos from 1.32.0 to 1.33.1 (#19551)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.32.0 to
1.33.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/releases">crate-ci/typos's
releases</a>.</em></p>
<blockquote>
<h2>v1.33.1</h2>
<h2>[1.33.1] - 2025-06-02</h2>
<h3>Fixes</h3>
<ul>
<li><em>(dict)</em> Don't correct <code>wasn't</code> to
<code>wasm't</code></li>
</ul>
<h2>v1.33.0</h2>
<h2>[1.33.0] - 2025-06-02</h2>
<h3>Features</h3>
<ul>
<li>Updated the dictionary with the <a
href="https://redirect.github.com/crate-ci/typos/issues/1290">May
2025</a> changes</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/blob/master/CHANGELOG.md">crate-ci/typos's
changelog</a>.</em></p>
<blockquote>
<h2>[1.33.1] - 2025-06-02</h2>
<h3>Fixes</h3>
<ul>
<li><em>(dict)</em> Don't correct <code>wasn't</code> to
<code>wasm't</code></li>
</ul>
<h2>[1.33.0] - 2025-06-02</h2>
<h3>Features</h3>
<ul>
<li>Updated the dictionary with the <a
href="https://redirect.github.com/crate-ci/typos/issues/1290">May
2025</a> changes</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b1ae8d918b"><code>b1ae8d9</code></a>
chore: Release</li>
<li><a
href="6c5d17de8e"><code>6c5d17d</code></a>
docs: Update changelog</li>
<li><a
href="0a237ba81a"><code>0a237ba</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1311">#1311</a>
from epage/wasn</li>
<li><a
href="79920cf069"><code>79920cf</code></a>
fix(dict): Don't correct <code>wasn't</code></li>
<li><a
href="e99b2b47d9"><code>e99b2b4</code></a>
chore: Release</li>
<li><a
href="2afc152754"><code>2afc152</code></a>
chore: Release</li>
<li><a
href="544a19b4ae"><code>544a19b</code></a>
docs: Update changelog</li>
<li><a
href="2e0ca28a95"><code>2e0ca28</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1310">#1310</a>
from epage/may</li>
<li><a
href="94eb4e7b40"><code>94eb4e7</code></a>
feat(dict): May 2025 updates</li>
<li><a
href="a4cce4ca70"><code>a4cce4c</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1308">#1308</a>
from crate-ci/renovate/schemars-0.x</li>
<li>Additional commits viewable in <a
href="https://github.com/crate-ci/typos/compare/v1.32.0...v1.33.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=crate-ci/typos&package-manager=github_actions&previous-version=1.32.0&new-version=1.33.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-06-09 23:40:47 +00:00
Lucas Franca
f5f092f2f3
Fix new typos (#19562)
# Objective

Fix new typos found on new version of `typos` (#19551)

## Solution

Fix typos
2025-06-09 22:55:14 +00:00
andriyDev
0381a798e2
Delete System::component_access(). (#19496)
# Objective

- Cleanup related to #19495.

## Solution

- Delete `System::component_access()`. It is redundant with
`System::component_access_set().combined_access()`.

## Testing

- None. There are no callers of this function.
2025-06-09 22:54:52 +00:00
Zion Klinger
a4dd873df8
Fix uninlined format argument lint (#19561)
# Objective
Fixes #19370 

## Solution
Implemented the lint as suggested by clippy.

## Testing
CI tests passed and lint was resolved.
2025-06-09 20:53:19 +00:00
urben1680
1294b71e35
Introduce CheckChangeTicks event that is triggered by World::check_change_ticks (#19274)
# Objective

In the past I had custom data structures containing `Tick`s. I learned
that these need to be regularly checked to clamp them. But there was no
way to hook into that logic so I abandoned storing ticks since then.

Another motivation to open this up some more is to be more able to do a
correct implementation of `System::check_ticks`.

## Solution

Add `CheckChangeTicks` and trigger it in `World::check_change_ticks`.
Make `Tick::check_tick` public.

This event makes it possible to store ticks in components or resources
and have them checked.

I also made `Schedules::check_change_ticks` public so users can store
schedules in custom resources/components for whatever reasons.

## Testing

The logic boils down to a single `World::trigger` call and I don't think
this needs more tests.

## Alternatives

Making this obsolete like with #15683.

---

## Showcase

From the added docs:

```rs
use bevy_ecs::prelude::*;
use bevy_ecs::component::CheckChangeTicks;

#[derive(Resource)]
struct CustomSchedule(Schedule);

let mut world = World::new();
world.add_observer(|tick: Trigger<CheckChangeTicks>, mut schedule: ResMut<CustomSchedule>| {
    schedule.0.check_change_ticks(tick.get());
});
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-09 20:44:49 +00:00
Marcel Müller
2768af5d2d
Allow not emitting BundleFromComponents with Bundle derive macro (#19249)
# Objective

Fixes #19136

## Solution

- Add a new container attribute which when set does not emit
`BundleFromComponents`

## Testing

- Did you test these changes?

Yes, a new test was added.

- Are there any parts that need more testing?

Since `BundleFromComponents` is unsafe I made extra sure that I did not
misunderstand its purpose. As far as I can tell, _not_ implementing it
is ok.

- How can other people (reviewers) test your changes? Is there anything
specific they need to know?

Nope

- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?

I don't think the platform is relevant

---



One thing I am not sure about is how to document this? I'll gladly add
it

---------

Signed-off-by: Marcel Müller <neikos@neikos.email>
2025-06-09 20:15:42 +00:00
Erick Z
b6e4d171b5
Adding PartialEq to some UI and Text types (#19552)
# Objective

- During the development of
[`bevy_flair`](https://github.com/eckz/bevy_flair) I found [some types
lacking
`PartialEq`](https://github.com/search?q=repo%3Aeckz%2Fbevy_flair%20%20%22Try%20to%20upstream%20it%20to%20bevy%22&type=code)
which made the unit testing a little bit inconvinient.

## Solution

- Adding `PartialEq` for the following types:
  - `LineHeight `
  - `TextShadow`
  - `NodeImageMode`

## Testing

- Letting github actions do the testing, this is not an invasive change
and `cargo run --bin ci` doesn't seem to work in `main` at the moment.
2025-06-09 20:08:17 +00:00
JMS55
bebe7c405d
Make camera controller not trigger change detection every frame (#19547)
Split off from https://github.com/bevyengine/bevy/pull/19058
2025-06-09 20:05:58 +00:00
JMS55
476e644a7d
Add extra buffer usages field to MeshAllocator (#19546)
Split off from https://github.com/bevyengine/bevy/pull/19058
2025-06-09 20:03:57 +00:00
Alice Cecile
155ebf7898
Write real docs for SystemSet (#19538)
# Objective

`SystemSet`s are surprisingly rich and nuanced, but are extremely poorly
documented.

Fixes #19536.

## Solution

Explain the basic concept of system sets, how to create them, and give
some opinionated advice about their more advanced functionality.

## Follow-up

I'd like proper module level docs on system ordering that I can link to
here, but they don't exist. Punting to follow-up!

---------

Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
2025-06-09 20:03:02 +00:00
ickshonpe
02fa833be1
Rename JustifyText to Justify (#19522)
# Objective

Rename `JustifyText`:

* The name `JustifyText` is just ugly.
* It's inconsistent since no other `bevy_text` types have a `Text-`
suffix, only prefix.
* It's inconsistent with the other text layout enum `Linebreak` which
doesn't have a prefix or suffix.

Fixes #19521.

## Solution

Rename `JustifyText` to `Justify`.

Without other context, it's natural to assume the name `Justify` refers
to text justification.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-09 19:59:48 +00:00
SpecificProtagonist
437c4d5b25
Minor clear color doc improvements (#19514) 2025-06-09 19:56:58 +00:00
theotherphil
5279863c42
Add missing docs for ImageLoader (#19499)
# Objective

Yet another tiny step towards
https://github.com/bevyengine/bevy/issues/3492.

---------

Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net>
2025-06-09 19:46:33 +00:00
andriyDev
f163649b48
Use component_access_set to determine the set of conflicting accesses between two systems. (#19495)
# Objective

- Fixes #4381

## Solution

- Replace `component_access` with `component_access_set` when
determining conflicting systems during schedule building.
- All `component_access()` impls just forward to
`&component_access_set().combined_access`, so we are essentially trading
`Access::is_compatible` for `FilteredAccessSet::is_compatible`.
- `FilteredAccessSet::get_conflicts` internally calls
`combined_access.is_compatible` as the first step, so we can remove that
redundant check.

## Testing

- Un-ignored a previously failing test now that it passes!
- Ran the `build_schedule` benchmark and got basically no change in the
results. Perhaps are benchmarks are just not targetted towards this
situation.
```
$ critcmp main fix-ambiguity -f 'build_schedule'
group                                          fix-ambiguity                          main
-----                                          -------------                          ----
build_schedule/1000_schedule                   1.00       2.9±0.02s        ? ?/sec    1.01       2.9±0.05s        ? ?/sec
build_schedule/1000_schedule_no_constraints    1.02     48.3±1.48ms        ? ?/sec    1.00     47.4±1.78ms        ? ?/sec
build_schedule/100_schedule                    1.00      9.9±0.17ms        ? ?/sec    1.06     10.5±0.32ms        ? ?/sec
build_schedule/100_schedule_no_constraints     1.00   804.7±21.85µs        ? ?/sec    1.03   828.7±19.36µs        ? ?/sec
build_schedule/500_schedule                    1.00    451.7±7.25ms        ? ?/sec    1.04   468.9±11.70ms        ? ?/sec
build_schedule/500_schedule_no_constraints     1.02     12.7±0.46ms        ? ?/sec    1.00     12.5±0.44ms        ? ?/sec
```
2025-06-09 19:40:52 +00:00
Eagster
064e5e48b4
Remove entity placeholder from observers (#19440)
# Objective

`Entity::PLACEHOLDER` acts as a magic number that will *probably* never
really exist, but it certainly could. And, `Entity` has a niche, so the
only reason to use `PLACEHOLDER` is as an alternative to `MaybeUninit`
that trades safety risks for logic risks.

As a result, bevy has generally advised against using `PLACEHOLDER`, but
we still use if for a lot internally. This pr starts removing internal
uses of it, starting from observers.

## Solution

Change all trigger target related types from `Entity` to
`Option<Entity>`

Small migration guide to come.

## Testing

CI

## Future Work

This turned a lot of code from 

```rust
trigger.target()
```

to 

```rust
trigger.target().unwrap()
```

The extra panic is no worse than before; it's just earlier than
panicking after passing the placeholder to something else.

But this is kinda annoying. 

I would like to add a `TriggerMode` or something to `Event` that would
restrict what kinds of targets can be used for that event. Many events
like `Removed` etc, are always triggered with a target. We can make
those have a way to assume Some, etc. But I wanted to save that for a
future pr.
2025-06-09 19:37:56 +00:00
SpecificProtagonist
860ff7819b
Remove workaround for image resize warning (#19397)
# Objective

Follow up for #19116 and #19098

## Testing

viewport_node example
2025-06-09 19:32:57 +00:00
Sigma-dev
8cd53162bf
Add a despawn_children method to EntityWorldMut and EntityCommands (#19283)
# Objective

At the moment, if someone wants to despawn all the children of an
entity, they would need to use `despawn_related::<Children>();`.
In my opinion, this makes a very common operation less easily
discoverable and require some understanding of Entity Relationships.

## Solution

Adding a `despawn_children ` makes a very simple, discoverable and
readable way to despawn all the children while maintaining cohesion with
other similar methods.

## Testing

The implementation itself is very simple as it simply wraps around
`despawn_related` with `Children` as the generic type.
I gave it a quick try by modifying the parenting example and it worked
as expected.

---------

Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2025-06-09 19:31:40 +00:00
theotherphil
20cd383b31
Mention Mut in QueryData docs, clarify behaviour of Mut vs &mut in Mut docs (#19254)
# Objective

- Fix https://github.com/bevyengine/bevy/issues/13843
- Clarify the difference between Mut and &mut when accessing query data

## Solution

- Mention `Mut` in `QueryData` docs as an example of a type that
implements this trait
- Give example of `iter_mut` vs `iter` access to `Mut` and `& mut`
parameters

## Testing

-
2025-06-09 19:21:04 +00:00
theotherphil
2bda628ecf
Clarify docs for transmute_lens functions (#19233)
# Objective

Make the restrictions of `transmute_lens` and related functions clearer.

Related issue: https://github.com/bevyengine/bevy/issues/12156
Related PR: https://github.com/bevyengine/bevy/pull/12157

## Solution

* Make it clearer that the set of returned entities is a subset of those
from the original query
* Move description of read/write/required access to a table
* Reference the new table in `transmute_lens` docs from the other
`transmute_lens*` functions

## Testing

cargo doc --open locally to check this render correctly

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-06-09 19:10:59 +00:00
ickshonpe
4836c7868c
Specialized UI transform (#16615)
# Objective

Add specialized UI transform `Component`s and fix some related problems:
* Animating UI elements by modifying the `Transform` component of UI
nodes doesn't work very well because `ui_layout_system` overwrites the
translations each frame. The `overflow_debug` example uses a horrible
hack where it copies the transform into the position that'll likely
cause a panic if any users naively copy it.
* Picking ignores rotation and scaling and assumes UI nodes are always
axis aligned.
* The clipping geometry stored in `CalculatedClip` is wrong for rotated
and scaled elements.
* Transform propagation is unnecessary for the UI, the transforms can be
updated during layout updates.
* The UI internals use both object-centered and top-left-corner-based
coordinates systems for UI nodes. Depending on the context you have to
add or subtract the half-size sometimes before transforming between
coordinate spaces. We should just use one system consistantly so that
the transform can always be directly applied.
* `Transform` doesn't support responsive coordinates.

## Solution

* Unrequire `Transform` from `Node`.
* New components `UiTransform`, `UiGlobalTransform`:
- `Node` requires `UiTransform`, `UiTransform` requires
`UiGlobalTransform`
- `UiTransform` is a 2d-only equivalent of `Transform` with a
translation in `Val`s.
- `UiGlobalTransform` newtypes `Affine2` and is updated in
`ui_layout_system`.
* New helper functions on `ComputedNode` for mapping between viewport
and local node space.
* The cursor position is transformed to local node space during picking
so that it respects rotations and scalings.
* To check if the cursor hovers a node recursively walk up the tree to
the root checking if any of the ancestor nodes clip the point at the
cursor. If the point is clipped the interaction is ignored.
* Use object-centered coordinates for UI nodes.
* `RelativeCursorPosition`'s coordinates are now object-centered with
(0,0) at the the center of the node and the corners at (±0.5, ±0.5).
* Replaced the `normalized_visible_node_rect: Rect` field of
`RelativeCursorPosition` with `cursor_over: bool`, which is set to true
when the cursor is over an unclipped point on the node. The visible area
of the node is not necessarily a rectangle, so the previous
implementation didn't work.

This should fix all the logical bugs with non-axis aligned interactions
and clipping. Rendering still needs changes but they are far outside the
scope of this PR.

Tried and abandoned two other approaches:
* New `transform` field on `Node`, require `GlobalTransform` on `Node`,
and unrequire `Transform` on `Node`. Unrequiring `Transform` opts out of
transform propagation so there is then no conflict with updating the
`GlobalTransform` in `ui_layout_system`. This was a nice change in its
simplicity but potentially confusing for users I think, all the
`GlobalTransform` docs mention `Transform` and having special rules for
how it's updated just for the UI is unpleasently surprising.
* New `transform` field on `Node`. Unrequire `Transform` on `Node`. New
`transform: Affine2` field on `ComputedNode`.
This was okay but I think most users want a separate specialized UI
transform components. The fat `ComputedNode` doesn't work well with
change detection.

Fixes #18929, #18930

## Testing

There is an example you can look at: 
```
cargo run --example ui_transform
```

Sometimes in the example if you press the rotate button couple of times
the first glyph from the top label disappears , I'm not sure what's
causing it yet but I don't think it's related to this PR.

##  Migration Guide
New specialized 2D UI transform components `UiTransform` and
`UiGlobalTransform`. `UiTransform` is a 2d-only equivalent of
`Transform` with a translation in `Val`s. `UiGlobalTransform` newtypes
`Affine2` and is updated in `ui_layout_system`.
`Node` now requires `UiTransform` instead of `Transform`. `UiTransform`
requires `UiGlobalTransform`.

In previous versions of Bevy `ui_layout_system` would overwrite UI
node's `Transform::translation` each frame. `UiTransform`s aren't
overwritten and there is no longer any need for systems that cache and
rewrite the transform for translated UI elements.

`RelativeCursorPosition`'s coordinates are now object-centered with
(0,0) at the the center of the node and the corners at (±0.5, ±0.5). Its
`normalized_visible_node_rect` field has been removed and replaced with
a new `cursor_over: bool` field which is set to true when the cursor is
hovering an unclipped area of the UI node.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-09 19:05:49 +00:00
JMS55
bf8868b7b7
Require naga_oil 0.17.1 (#19550)
Split off from https://github.com/bevyengine/bevy/pull/19058

The patch should've been picked up anyways, but now it's required.
2025-06-09 04:54:29 +00:00
JMS55
16440be327
Add CameraMainTextureUsages helper method (#19549)
Split off from https://github.com/bevyengine/bevy/pull/19058
2025-06-09 04:54:14 +00:00
JMS55
ec307bcb9f
Add more wgpu helpers/types (#19548)
Split off from https://github.com/bevyengine/bevy/pull/19058
2025-06-09 04:54:02 +00:00
re0312
56f26cfb02
Unify system state (#19506)
# Objective

- A preparation for the 'system as entities'
- The current system has a series of states such as `is_send`,
`is_exclusive`, `has_defered`, As `system as entites` landed, it may
have more states. Using Bitflags to unify all states is a more concise
and performant approach

## Solution

- Using Bitflags to  unify system state.
2025-06-08 18:18:43 +00:00
SpecificProtagonist
b9754f963f
Gradients example: Fit in initial window (#19520)
# Objective

When running the `gradient` example, part of the content doesn't fit
within the initial window:
![Screenshot from 2025-06-07
11-42-59](https://github.com/user-attachments/assets/a54223db-0223-4a6e-b8e7-adb306706b28)

The UI requires 1830×930 pixels, but the initial window size is
1280×720.

## Solution

Make ui elements smaller:
![Screenshot from 2025-06-07
11-42-13](https://github.com/user-attachments/assets/c1afc01e-51be-4295-8c0f-6a983fbb0969)

Alternative: Use a larger initial window size. I decided against this
because that would make the examples less uniform, make the code less
focused on gradients and not help on web.
2025-06-08 17:26:02 +00:00
Kristoffer Søholm
2c37bdeb47
Fix PickingInteraction change detection (#19488)
# Objective

Fixes #19464

## Solution

Instead of clearing previous `PickingInteractions` before updating, we
clear them last for those components that weren't updated, and use
`set_if_neq` when writing.

## Testing

I tried the sprite_picking example and it still works. 

You can add the following system to picking examples to check that
change detection works as intended:

```rust
fn print_picking(query: Query<(Entity, &PickingInteraction), Changed<PickingInteraction>>) {
    for (entity, interaction) in &query {
        println!("{entity} {interaction:?}");
    }
}
```
2025-06-08 16:29:13 +00:00
theotherphil
6b5289bd5e
deny(missing_docs) for bevy_ecs_macros (#19523)
# Objective

Deny missing docs for bevy_ecs_macros, towards
https://github.com/bevyengine/bevy/issues/3492.

## Solution

More docs of the form

```
/// Does the thing
fn do_the_thing() {}
```

But I don't think the derive macros are where anyone is going to be
looking for details of these concepts and deny(missing_docs) inevitably
results in some items having noddy docs.
2025-06-08 16:28:31 +00:00
Yuki Osada
a16adc751b
Update add flake.nix example (#19321)
# Objective

I can't build a project using bevy under the environment of NixOS, so I
have to create flake.nix file.

## Solution

I add flake.nix example to `linux_dependencies.md`.

## Testing

I checked my NixOS environment in a project using bevy and booted the
project's game successfully.

---

## Showcase

<details>
  <summary>Click to view showcase</summary>

1. Create a GitHub project using bevy.
2. Add a flake.nix file.
3.  Commit to add this file to the GitHub repository.
4. Run `nix develop`

</details>

---------

Co-authored-by: nukanoto <me@nukanoto.net>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Ilia <43654815+istudyatuni@users.noreply.github.com>
2025-06-08 02:15:19 +00:00
JoshValjosh
ddee5cca85
Improve Bevy's double-precision story for third-party crates (#19194)
# Objective

Certain classes of games, usually those with enormous worlds, require
some amount of support for double-precision. Libraries like `big_space`
exist to allow for large worlds while integrating cleanly with Bevy's
primarily single-precision ecosystem, but even then, games will often
still work directly in double-precision throughout the part of the
pipeline that feeds into the Bevy interface.

Currently, working with double-precision types in Bevy is a pain. `glam`
provides types like `DVec3`, but Bevy doesn't provide double-precision
analogs for `glam` wrappers like `Dir3`. This is mostly because doing so
involves one of:

- code duplication
- generics
- templates (like `glam` uses)
- macros

Each of these has issues that are enough to be deal-breakers as far as
maintainability, usability or readability. To work around this, I'm
putting together `bevy_dmath`, a crate that duplicates `bevy_math` types
and functionality to allow downstream users to enjoy the ergonomics and
power of `bevy_math` in double-precision. For the most part, it's a
smooth process, but in order to fully integrate, there are some
necessary changes that can only be made in `bevy_math`.

## Solution

This PR addresses the first and easiest issue with downstream
double-precision math support: `VectorSpace` currently can only
represent vector spaces over `f32`. This automatically closes the door
to double-precision curves, among other things. This restriction can be
easily lifted by allowing vector spaces to specify the underlying scalar
field. This PR adds a new trait `ScalarField` that satisfies the
properties of a scalar field (the ones that can be upheld statically)
and adds a new associated type `type Scalar: ScalarField` to
`VectorSpace`. It's mostly an unintrusive change. The biggest annoyances
are:

- it touches a lot of curve code
- `bevy_math::ops` doesn't support `f64`, so there are some annoying
workarounds

As far as curves code, I wanted to make this change unintrusive and
bite-sized, so I'm trying to touch as little code as possible. To prove
to myself it can be done, I went ahead and (*not* in this PR) migrated
most of the curves API to support different `ScalarField`s and it went
really smoothly! The ugliest thing was adding `P::Scalar: From<usize>`
in several places. There's an argument to be made here that we should be
using `num-traits`, but that's not immediately relevant. The point is
that for now, the smallest change I could make was to go into every
curve impl and make them generic over `VectorSpace<Scalar = f32>`.
Curves work exactly like before and don't change the user API at all.

# Follow-up

- **Extend `bevy_math::ops` to work with `f64`.** `bevy_math::ops` is
used all over, and if curves are ever going to support different
`ScalarField` types, we'll need to be able to use the correct `std` or
`libm` ops for `f64` types as well. Adding an `ops64` mod turned out to
be really ugly, but I'll point out the maintenance burden is low because
we're not going to be adding new floating-point ops anytime soon.
Another solution is to build a floating-point trait that calls the right
op variant and impl it for `f32` and `f64`. This reduces maintenance
burden because on the off chance we ever *do* want to go modify it, it's
all tied together: you can't change the interface on one without
changing the trait, which forces you to update the other. A third option
is to use `num-traits`, which is basically option 2 but someone else did
the work for us. They already support `no_std` using `libm`, so it would
be more or less a drop-in replacement. They're missing a couple
floating-point ops like `floor` and `ceil`, but we could make our own
floating-point traits for those (there's even the potential for
upstreaming them into `num-traits`).
- **Tweak curves to accept vector spaces over any `ScalarField`.**
Curves are ready to support custom scalar types as soon as the bullet
above is addressed. I will admit that the code is not as fun to look at:
`P::Scalar` instead of `f32` everywhere. We could consider an alternate
design where we use `f32` even to interpolate something like a `DVec3`,
but personally I think that's a worse solution than parameterizing
curves over the vector space's scalar type. At the end of the day, it's
not really bad to deal with in my opinion... `ScalarType` supports
enough operations that working with them is almost like working with raw
float types, and it unlocks a whole ecosystem for games that want to use
double-precision.
2025-06-08 02:02:47 +00:00
urben1680
76b8310da5
Replace (Partial)Ord for EntityGeneration with corrected standalone method (#19432)
# Objective

#19421 implemented `Ord` for `EntityGeneration` along the lines of [the
impl from
slotmap](https://docs.rs/slotmap/latest/src/slotmap/util.rs.html#8):
```rs
/// Returns if a is an older version than b, taking into account wrapping of
/// versions.
pub fn is_older_version(a: u32, b: u32) -> bool {
    let diff = a.wrapping_sub(b);
    diff >= (1 << 31)
}
```

But that PR and the slotmap impl are different:

**slotmap impl**
- if `(1u32 << 31)` is greater than `a.wrapping_sub(b)`, then `a` is
older than `b`
- if `(1u32 << 31)` is equal to `a.wrapping_sub(b)`, then `a` is older
than `b`
- if `(1u32 << 31)` is less than `a.wrapping_sub(b)`, then `a` is equal
or newer than `b`

**previous PR impl**
- if `(1u32 << 31)` is greater than `a.wrapping_sub(b)`, then `a` is
older than `b`
- if `(1u32 << 31)` is equal to `a.wrapping_sub(b)`, then `a` is equal
to `b` ⚠️
- if `(1u32 << 31)` is less than `a.wrapping_sub(b)`, then `a` is newer
than `b` ⚠️

This ordering is also not transitive, therefore it should not implement
`PartialOrd`.

## Solution

Fix the impl in a standalone method, remove the `Partialord`/`Ord`
implementation.

## Testing

Given the first impl was wrong and got past reviews, I think a new unit
test is justified.
2025-06-07 22:29:13 +00:00
andriyDev
de79d3f363
Mention in the docs for pointer events that these are in screen-space. (#19518)
# Objective

- Fixes #18109.

## Solution

- All these docs now mention screen-space vs world-space.
- `start_pos` and `latest_pos` both link to `viewport_to_world` and
`viewport_to_world_2d`.
- The remaining cases are all deltas. Unfortunately `Camera` doesn't
have an appropriate method for these cases, and implementing one would
be non-trivial (e.g., the delta could have a different world-space size
based on the depth). For these cases, I just link to `Camera` and
suggest using some of its methods. Not a great solution, but at least it
gets users on the correct track.
2025-06-06 22:20:14 +00:00
Alice Cecile
e1230fdc54
Remove re-exports of cosmic_text types (#19516)
# Objective

As discussed in #19285, we do a poor job at keeping the namespace tidy
and free of duplicates / user-conflicting names in places. `cosmic_text`
re-exports were the worst offender.

## Solution

Remove the re-exports completely. While the type aliases were quite
thoughtful, they weren't used in any of our code / API.
2025-06-06 21:49:02 +00:00
re0312
5df9c53977
Fix regression on the get/get_mut/get_not_found (#19505)
# Objective

- Partial fix #19504
- As more features were added to Bevy ECS, certain core hot-path
function calls exceeded LLVM's automatic inlining threshold, leading to
significant performance regressions in some cases.



## Solution

- inline more functions.


## Performance
This brought nearly 3x improvement in Windows bench (using Sander's
testing code)

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-06 21:04:27 +00:00
Eagster
8ad7118443
Only get valid component ids (#19510)
# Objective

- #19504 showed a 11x regression in getting component values for
unregistered components. This pr should fix that and improve others a
little too.
- This is some cleanup work from #18173 .

## Solution

- Whenever we expect a component value to exist, we only care about
fully registered components, not queued to be registered components
since, for the value to exist, it must be registered.
- So we can use the faster `get_valid_*` instead of `get_*` in a lot of
places.
- Also found a bug where `valid_*` did not forward to `get_valid_*`
properly. That's fixed.

## Testing

CI
2025-06-06 20:59:57 +00:00
Alice Cecile
7ac2ae5713
Allow new mismatched_lifetime_syntaxes lint (#19515)
# Objective

The new nightly lint produces [unhelpful, noisy
output](http://github.com/bevyengine/bevy/actions/runs/15491867876/job/43620116435?pr=19510)
that makes lifetimes more prominent in our library code than we
generally find helpful.

This needs to be fixed or allowed, in order to unbreak CI for every PR
in this repo.

## Solution

Blanket allow the lint at the workspace level.

## Testing

Let's see if CI passes!
2025-06-06 20:14:15 +00:00
robtfm
3dc6a07d27
generic component propagation (#17575)
# Objective

add functionality to allow propagating components to children. requested
originally for `RenderLayers` but can be useful more generally.

## Solution

- add `HierarchyPropagatePlugin<C, F=()>` which schedules systems to
propagate components through entities matching `F`
- add `Propagate<C: Component + Clone + PartialEq>` which will cause `C`
to be added to all children
more niche features:
- add `PropagateStop<C>` which stops the propagation at this entity
- add `PropagateOver<C>` which allows the propagation to continue to
children, but doesn't add/remove/modify a `C` on this entity itself

## Testing

see tests inline

## Notes

- could happily be an out-of-repo plugin
- not sure where it lives: ideally it would be in `bevy_ecs` but it
requires a `Plugin` so I put it in `bevy_app`, doesn't really belong
there though.
- i'm not totally up-to-date on triggers and observers so possibly this
could be done more cleanly, would be very happy to take review comments
- perf: this is pretty cheap except for `update_reparented` which has to
check the parent of every moved entity. since the entirety is opt-in i
think it's acceptable but i could possibly use `(Changed<Children>,
With<Inherited<C>>)` instead if it's a concern
2025-06-06 00:02:02 +00:00
Carter Anderson
7e9d6d852b
bevyengine.org -> bevy.org (#19503)
We have acquired [bevy.org](https://bevy.org) and the migration has
finished! Meaning we can now update all of the references in this repo.
2025-06-05 23:09:28 +00:00
Carter Anderson
fdf5dd677f
Update FUNDING.yml 2025-06-05 15:04:57 -07:00
ZoOL
a35eed0ea4
fix: Ensure linear volume subtraction does not go below zero (#19423)
fix: [Ensure linear volume subtraction does not go below zero
](https://github.com/bevyengine/bevy/issues/19417)

## Solution
- Clamp the result of linear volume subtraction to a minimum of 0.0
- Add a new test case to verify behavior when subtracting beyond zero

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2025-06-05 03:59:20 +00:00
theotherphil
d0f1b3e9f1
Add a few missing doc comments in bevy_image (#19493)
# Objective

Another tiny step towards
https://github.com/bevyengine/bevy/issues/3492.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-04 21:12:02 +00:00
theotherphil
476d79d821
deny(missing_docs) for bevy_state (#19492)
# Objective

Deny missing docs in bevy_state, towards
https://github.com/bevyengine/bevy/issues/3492.
2025-06-04 20:43:47 +00:00
Chris Russell
bd4c960f26
Mention Option and When in the error message for a failing system parameter (#19490)
# Objective

Help users discover how to use `Option<T>` and `When<T>` to handle
failing parameters.

## Solution

Have the error message for a failed parameter mention that `Option<T>`
and `When<T>` can be used to handle the failure.

## Showcase

```
Encountered an error in system `system_name`: Parameter `Res<ResourceType>` failed validation: Resource does not exist
If this is an expected state, wrap the parameter in `Option<T>` and handle `None` when it happens, or wrap the parameter in `When<T>` to skip the system when it happens.
```
2025-06-04 16:39:54 +00:00
Kristoffer Søholm
6fee6fe827
Add get_mut_untracked to Assets (#19487)
# Objective

Fixes #13104

## Solution

Add a `get_mut_untracked` method to `Assets`
2025-06-04 16:34:27 +00:00
theotherphil
c5dcef5e61
deny(missing_docs) for bevy_diagnostic (#19482)
# Objective

Deny missing docs in bevy_diagnostic, towards
https://github.com/bevyengine/bevy/issues/3492.
2025-06-04 01:30:10 +00:00
theotherphil
e7a309ff5f
deny(missing_docs) for bevy_derive (#19483)
# Objective

Deny missing docs in bevy_derive, towards
https://github.com/bevyengine/bevy/issues/3492.
2025-06-04 00:06:32 +00:00
andriyDev
723b52abd3
Allow returning an error from labeled_asset_scope. (#19449)
# Objective

- `LoadContext::labeled_asset_scope` cannot return errors back to the
asset loader. This means users that need errors need to fall back to
using the raw `begin_labeled_asset` and `add_loaded_labeled_asset`,
which is more error-prone.

## Solution

- Allow returning a (generic) error from `labeled_asset_scope`.
- This has the unfortunate side effect that closures which don't return
any errors need to A) return Ok at the end, B) need to specify an error
type (e.g., `()`).

---

## Showcase

```rust
// impl AssetLoader for MyLoader
let handle = load_context.labeled_asset_scope("MySubasset", |mut load_context| {
  if !some_precondition {
    return Err(ThingsDontMakeSenseError);
  }
  let handle = load_context.add_labeled_asset("MySubasset/Other", SomeOtherThing(456));
  Ok(Something{ id: 123, handle })
})?;
```
2025-06-04 00:00:32 +00:00
SpecificProtagonist
5561b40bdf
Use BevyError for AssetLoader::Error (#19478)
# Objective

Allow using `BevyResult` in `AssetLoader`s for consistency. Currently,
it converts errors into `Box<dyn core::error::Error + Send + Sync +
'static>`, which is essentially a `BevyError` without the optional
backtrace functionality.

## Solution

I don't think needs a migration guide as any type that satisfies
`Into<Box<dyn core::error::Error + Send + Sync + 'static>>` also
satisfies `Into<BevyError>`.
2025-06-03 23:58:44 +00:00
François Mockers
7a7bff8c17
Hot patching systems with subsecond (#19309)
# Objective

- Enable hot patching systems with subsecond
- Fixes #19296 

## Solution

- First commit is the naive thin layer
- Second commit only check the jump table when the code is hot patched
instead of on every system execution
- Depends on https://github.com/DioxusLabs/dioxus/pull/4153 for a nicer
API, but could be done without
- Everything in second commit is feature gated, it has no impact when
the feature is not enabled

## Testing

- Check dependencies without the feature enabled: nothing dioxus in tree
- Run the new example: text and color can be changed

---------

Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
2025-06-03 21:12:38 +00:00
re0312
50aa40e980
Trigger ArchetypeCreated event when new archetype is created (#19455)
# Objective

- Part 1 of #19454 .
- Split from PR #18860(authored by @notmd) for better review and limit
implementation impact. so all credit for this work belongs to @notmd .

## Solution

- Trigger `ArchetypeCreated ` when new archetype is createed

---------

Co-authored-by: mgi388 <135186256+mgi388@users.noreply.github.com>
2025-06-02 22:27:45 +00:00
maya
f93e5c5622
Exclude ctrlc from bevy_app for the Nintendo 3DS (#19453)
## Background/motivation

The Nintendo 3DS is supported by the tier 3 rust target
[armv6k-nintendo-3ds](https://doc.rust-lang.org/rustc/platform-support/armv6k-nintendo-3ds.html#armv6k-nintendo-3ds).
Bevy does not officially support the device, but as more of bevy becomes
`no_std` compatible, more targets are being partially supported (e.g.
GBA - https://github.com/bevyengine/bevy/discussions/10680,
https://github.com/bushrat011899/bevy_mod_gba) officially or not.

The Nintendo 3DS runs Horizon as its OS which is
[unix-based](4d08223c05/compiler/rustc_target/src/spec/targets/armv6k_nintendo_3ds.rs (L34)),
and the above target (at least partially) supports rust std. It makes
sense that you would want to use it, since the 3DS supports things like
filesystem reads and the system clock.

## Problem

Unlike standard unix targets, armv6k-nintendo-3ds is not one that can
use/build the the `ctrlc` dependency in `bevy_app` which is enabled by
the bevy `std` cargo feature.

Without the `std` feature flag, scheduled systems panic without
providing another way for bevy to tick using the `Instant` type (like
you might for a
[GBA](72d8bbf47b/src/time.rs (L36))).

<details>

<summary>Example</summary>

```
    Finished `dev` profile [optimized + debuginfo] target(s) in 1m 39s
Building smdh: /home/maya/repos/hyperspace-dj/target/armv6k-nintendo-3ds/debug/hyperspace-dj.smdh
Building 3dsx: /home/maya/repos/hyperspace-dj/target/armv6k-nintendo-3ds/debug/hyperspace-dj.3dsx
Adding RomFS from /home/maya/repos/hyperspace-dj/romfs
Running 3dslink
Sending hyperspace-dj.3dsx, 7172344 bytes
2777346 sent (38.72%), 233 blocks
starting server
server active ...
hii we'are about the to start the bevy app

thread 'main' panicked at /home/maya/repos/bevy/crates/bevy_platform/src/time/fallback.rs:177:13:
An elapsed time getter has not been provided to `Instant`. Please use `Instant::set_elapsed(...)` before calling `Instant::now()`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

</details>

## Solution

This PR simply excludes the `ctrlc` dependency and its uses in
`bevy_app` for the 3DS target (`horizon`) with an addition to its
existing feature flags.

After this fix, we can use the `std` feature, and regular scheduled
systems no longer panic because of missing `Instant` (system clock)
support.

## Testing

I compiled and ran a binary with the modified version of bevy, using
`no_default_features` and feature flags `default_no_std` and `std` on a
physical 3DS (classic) using homebrew and `cargo-3ds`.

Toolchain:
[armv6k-nintendo-3ds](https://doc.rust-lang.org/rustc/platform-support/armv6k-nintendo-3ds.html#armv6k-nintendo-3ds)
(nightly-2025-03-31)
Project reference:
440fc10184

## Considerations

It could be that we don't want to add specific exceptions inside bevy to
support specific hardware with weird quirks inside general bevy code,
but it's not obvious to me what we should use instead of an exception to
(pre-existing) target cfg: every change here is merely an addition to a
cfg that already checks for both the target family and the `std` flag.

It is not clear to me if this PR is exhaustive enough to be considered
an adequate solution for the larger goal of partially supporting the
3DS, but it seems to be a step in the right direction because it at
least lets trivial App::run setups with scheduled systems work.
2025-06-02 22:21:57 +00:00
zacryol
4d4170d834
Implement IntoIterator for Populated and borrows (#19441)
# Objective

`Populated`, a loose wrapper around `Query`, does not implement
`IntoIterator`, requiring either a deref or `into_inner()` call to
access the `Query` and iterate over that.

## Solution

This pr implements `IntoIterator` for `Populated`, `&Populated`, and
`&mut Populated`, each of which forwards the call to the inner `Query`.
This allows the `Populated` to be used directly for any API that takes
an `impl IntoIterator`.

## Testing

`cargo test` was run on the `bevy_ecs` crate
```
test result: ok. 390 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 46.38s
```
2025-06-02 22:19:43 +00:00
AlephCubed
415f6d8ca7
Simplified on_replace and on_despawn relationship hooks. (#19378)
Fixes #18364.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-02 22:15:18 +00:00
theotherphil
3d7486b019
Clarify PartialReflect::apply docs (#19250)
# Objective

Fix https://github.com/bevyengine/bevy/issues/18558

## Solution

* Replace `T` in docs with `Self`
* Fix broken link - replace "introspection subtraits" with "reflection
subtraits"
* Added missing `Set` variant to the list of per-`ReflectKind`-variation
behaviours

## Testing

cargo doc --serve locally to check that the broken link is fixed

---------

Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2025-06-02 22:12:16 +00:00
Lucas
05e0315355
add 2d_on_ui example (#18513)
hello ! im not english native, don't hesitate to correct my code
comments.

# Objective

a simple example for new users. This is a question asked a lot, and i
struggled to do it at first too.
see https://github.com/bevyengine/bevy/discussions/11223

## Showcase


![image](https://github.com/user-attachments/assets/179ec3ad-add5-4963-ab32-3ad1cc9df15c)

---------

Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
2025-06-02 22:11:32 +00:00
JMS55
8255e6cda9
Make TAA non-experimental, fixes (#18349)
The first 4 commits are designed to be reviewed independently.

- Mark TAA non-experimental now that motion vectors are written for
skinned and morphed meshes, along with skyboxes, and add it to
DefaultPlugins
- Adjust halton sequence to match what DLSS is going to use, doesn't
really affect anything, but may as well
- Make MipBias a required component on TAA instead of inserting it in
the render world
- Remove MipBias, TemporalJitter, RenderLayers, etc from the render
world if they're removed from the main world (fixes a retained render
world bug)
- Remove TAA components from the render world properly if
TemporalAntiAliasing is removed from the main world (fixes a retained
render world bug)
- extract_taa_settings() now has to query over `Option<&mut
TemporalAntiAliasing>`, which will match every single camera, in order
to cover cameras that had TemporalAntiAliasing removed this frame. This
kind of sucks, but I can't think of anything better.
- We probably have the same bug with every other rendering feature
component we have.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-02 16:04:08 +00:00
Rob Parrett
dc4737923c
Add resize_in_place to Image (#19410)
# Objective

Ultimately, I'd like to modify our font atlas creation systems so that
they are able to resize the font atlases as more glyphs are added. At
the moment, they create a new 512x512 atlas every time one fills up.
With large font sizes and many glyphs, your glyphs may end up spread out
across several atlases.

The goal would be to render text more efficiently, because glyphs spread
across fewer textures could benefit more from batching.

`AtlasAllocator` already has support for growing atlases, but we don't
currently have a way of growing a texture while keeping the pixel data
intact.

## Solution

Add a new method to `Image`: `resize_in_place` and a test for it.

## Testing

Ran the new test, and also a little demo comparing this side-by-side
with `resize`.

<details>
<summary>Expand Code</summary>

```rust
//! Testing ground for #19410

use bevy::prelude::*;
use bevy_render::render_resource::Extent3d;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(Update, test)
        .init_resource::<Size>()
        .insert_resource(FillColor(Hsla::hsl(0.0, 1.0, 0.7)))
        .run();
}

#[derive(Resource, Default)]
struct Size(Option<UVec2>);
#[derive(Resource)]
struct FillColor(Hsla);
#[derive(Component)]
struct InPlace;

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2d);

    commands.spawn((
        Transform::from_xyz(220.0, 0.0, 0.0),
        Sprite::from_image(asset_server.load("branding/bevy_bird_dark.png")),
    ));

    commands.spawn((
        InPlace,
        Transform::from_xyz(-220.0, 0.0, 0.0),
        Sprite::from_image(asset_server.load("branding/icon.png")),
    ));
}

fn test(
    sprites: Query<(&Sprite, Has<InPlace>)>,
    mut images: ResMut<Assets<Image>>,
    mut new_size: ResMut<Size>,
    mut dir: Local<IVec2>,
    mut color: ResMut<FillColor>,
) -> Result {
    for (sprite, in_place) in &sprites {
        let image = images.get_mut(&sprite.image).ok_or("Image not found")?;
        let size = new_size.0.get_or_insert(image.size());

        if *dir == IVec2::ZERO {
            *dir = IVec2::splat(1);
        }

        *size = size.saturating_add_signed(*dir);

        if size.x > 400 || size.x < 150 {
            *dir = *dir * -1;
        }

        color.0 = color.0.rotate_hue(1.0);

        if in_place {
            image.resize_in_place_2d(
                Extent3d {
                    width: size.x,
                    height: size.y,
                    ..default()
                },
                &Srgba::from(color.0).to_u8_array(),
            )?;
        } else {
            image.resize(Extent3d {
                width: size.x,
                height: size.y,
                ..default()
            });
        }
    }

    Ok(())
}
```
</details>


https://github.com/user-attachments/assets/6b2d0ec3-6a6e-4da1-98aa-29e7162f16fa

## Alternatives

I think that this might be useful functionality outside of the font
atlas scenario, but we *could* just increase the initial font atlas
size, make it configurable, and/or size font atlases according to device
limits. It's not totally clear to me how to accomplish that last idea.
2025-05-31 21:55:11 +00:00
AlephCubed
b993202d79
Refactor state scoped events to match entities. (#19435)
This adds support for clearing events when **entering** a state (instead
of just when exiting) and updates the names to match
`DespawnOnExitState`.

Before:
```rust
app.add_state_scoped_event::<MyGameEvent>(GameState::Play);
```
After:
```rust
app
  .add_event::<MyGameEvent>()
  .clear_events_on_exit_state::<MyGameEvent>(GameState::Play);
```
2025-05-31 20:14:14 +00:00
Eagster
61bd3af7c7
Remove invalid entity locations (#19433)
# Objective

This is the first step of #19430 and is a follow up for #19132.

Now that `ArchetypeRow` has a niche, we can use `Option` instead of
needing `INVALID` everywhere.

This was especially concerning since `INVALID` *really was valid!*

Using options here made the code clearer and more data-driven. 

## Solution

Replace all uses of `INVALID` entity locations (and archetype/table
rows) with `None`.

## Testing

CI

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-31 16:34:33 +00:00
ickshonpe
43b8fbda93
Unrequire VisibilityClass from Node (#17918)
# Objective

The UI doesn't use `ViewVisibility` so it doesn't do anything.

## Solution

Remove it.
2025-05-31 08:18:01 +00:00
Rob Parrett
0fca6938ea
Fix missing meta files breaking Bevy on itch (#19268)
# Objective

Fixes #19029 (also maybe sorta #18002, but we may want to handle the SPA
issue I outlined there more gracefully?)


## Solution

The most minimal / surgical approach I could think of, hopefully
cherry-pickable for a point release.

It seems that it's not *entirely* crazy for web services to return 403
for an item that was not found. Here's an example from [Amazon
CloudFront
docs](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/http-403-permission-denied.html#s3-origin-403-error).
If it is somewhat common for web services to behave this way, then I
think it's best to also treat these responses as if they were "not
found."

I was previously of the opinion that any 400 level error "might as well"
get this treatment, but I'm now thinking that's probably overkill and
there are quite a few 400 level statuses that would indicate some
problem that needs to be fixed, and interpreting these as "not found"
might add confusion to the debugging process.

## Testing

Tested this with a web server that returns 403 for requests to meta
files.

```bash
cargo run -p build-wasm-example -- --api webgl2 sprite && \
open "http://localhost:4000" && \
python3 test_403.py examples/wasm
```
`test_403.py`:
```python
from http.server import HTTPServer, SimpleHTTPRequestHandler
import os
import sys


class CustomHandler(SimpleHTTPRequestHandler):
    def do_GET(self):
        if self.path.endswith(".meta"):
            self.send_response(403)
            self.send_header("Content-type", "text/plain")
            self.end_headers()
            self.wfile.write(b"403 Forbidden: Testing.\n")
        else:
            super().do_GET()


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} <directory>")
        sys.exit(1)

    os.chdir(sys.argv[1])

    server_address = ("", 4000)
    httpd = HTTPServer(server_address, CustomHandler)
    httpd.serve_forever()
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-05-30 20:33:47 +00:00
Sarthak Singh
d82d3238d1
Fixed memory leak in bindless material (#19041)
# Objective

Fixed #19035. Fixed #18882. It consisted of two different bugs:
- The allocations where being incremented even when a Data binding was
created.
- The ref counting on the binding was broken.

## Solution

- Stopped incrementing the allocations when a data binding was created.
- Rewrote the ref counting code to more reliably track the ref count.

## Testing

Tested my fix for 10 minutes with the `examples/3d/animated_material.rs`
example. I changed the example to spawn 51x51 meshes instead of 3x3
meshes to heighten the effects of the bug.

My branch: (After 10 minutes of running the modified example)
GPU: 172 MB
CPU: ~700 MB

Main branch: (After 2 minutes of running the modified example, my
computer started to stutter so I had to end it early)
GPU: 376 MB
CPU: ~1300 MB
2025-05-30 19:36:56 +00:00
robtfm
c617fc49ae
fix distinct directional lights per view (#19147)
# Objective

after #15156 it seems like using distinct directional lights on
different views is broken (and will probably break spotlights too). fix
them

## Solution

the reason is a bit hairy so with an example:

- camera 0 on layer 0
- camera 1 on layer 1
- dir light 0 on layer 0 (2 cascades)
- dir light 1 on layer 1 (2 cascades)

in render/lights.rs:
- outside of any view loop, 
- we count the total number of shadow casting directional light cascades
(4) and assign an incrementing `depth_texture_base_index` for each (0-1
for one light, 2-3 for the other, depending on iteration order) (line
1034)
- allocate a texture array for the total number of cascades plus
spotlight maps (4) (line 1106)

- in the view loop, for directional lights we 
  - skip lights that don't intersect on renderlayers (line 1440)
- assign an incrementing texture layer to each light/cascade starting
from 0 (resets to 0 per view) (assigning 0 and 1 each time for the 2
cascades of the intersecting light) (line 1509, init at 1421)

then in the rendergraph:
- camera 0 renders the shadow map for light 0 to texture indices 0 and 1
- camera 0 renders using shadows from the `depth_texture_base_index`
(maybe 0-1, maybe 2-3 depending on the iteration order)

- camera 1 renders the shadow map for light 1 to texture indices 0 and 1
- camera 0 renders using shadows from the `depth_texture_base_index`
(maybe 0-1, maybe 2-3 depending on the iteration order)

issues:
- one of the views uses empty shadow maps (bug)
- we allocated a texture layer per cascade per light, even though not
all lights are used on all views (just inefficient)
- I think we're allocating texture layers even for lights with
`shadows_enabled: false` (just inefficient)

solution:
- calculate upfront the view with the largest number of directional
cascades
- allocate this many layers (plus layers for spotlights) in the texture
array
- keep using texture layers 0..n in the per-view loop, but build
GpuLights.gpu_directional_lights within the loop too so it refers to the
same layers we render to

nice side effects: 
- we can now use `max_texture_array_layers / MAX_CASCADES_PER_LIGHT`
shadow-casting directional lights per view, rather than overall.
- we can remove the `GpuDirectionalLight::skip` field, since the gpu
lights struct is constructed per view

a simpler approach would be to keep everything the same, and just
increment the texture layer index in the view loop even for
non-intersecting lights. this pr reduces the total shadowmap vram used
as well and isn't *much* extra complexity. but if we want something less
risky/intrusive for 16.1 that would be the way.

## Testing

i edited the split screen example to put separate lights on layer 1 and
layer 2, and put the plane and fox on both layers (using lots of
unrelated code for render layer propagation from #17575).
without the fix the directional shadows will only render on one of the
top 2 views even though there are directional lights on both layers.

```rs
//! Renders two cameras to the same window to accomplish "split screen".

use std::f32::consts::PI;

use bevy::{
    pbr::CascadeShadowConfigBuilder, prelude::*, render:📷:Viewport, window::WindowResized,
};
use bevy_render::view::RenderLayers;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(HierarchyPropagatePlugin::<RenderLayers>::default())
        .add_systems(Startup, setup)
        .add_systems(Update, (set_camera_viewports, button_system))
        .run();
}

/// set up a simple 3D scene
fn setup(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let all_layers = RenderLayers::layer(1).with(2).with(3).with(4);

    // plane
    commands.spawn((
        Mesh3d(meshes.add(Plane3d::default().mesh().size(100.0, 100.0))),
        MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
        all_layers.clone()
    ));

    commands.spawn((
        SceneRoot(
            asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")),
        ),
        Propagate(all_layers.clone()),
    ));

    // Light
    commands.spawn((
        Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
        DirectionalLight {
            shadows_enabled: true,
            ..default()
        },
        CascadeShadowConfigBuilder {
            num_cascades: if cfg!(all(
                feature = "webgl2",
                target_arch = "wasm32",
                not(feature = "webgpu")
            )) {
                // Limited to 1 cascade in WebGL
                1
            } else {
                2
            },
            first_cascade_far_bound: 200.0,
            maximum_distance: 280.0,
            ..default()
        }
        .build(),
        RenderLayers::layer(1),
    ));

    commands.spawn((
        Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
        DirectionalLight {
            shadows_enabled: true,
            ..default()
        },
        CascadeShadowConfigBuilder {
            num_cascades: if cfg!(all(
                feature = "webgl2",
                target_arch = "wasm32",
                not(feature = "webgpu")
            )) {
                // Limited to 1 cascade in WebGL
                1
            } else {
                2
            },
            first_cascade_far_bound: 200.0,
            maximum_distance: 280.0,
            ..default()
        }
        .build(),
        RenderLayers::layer(2),
    ));

    // Cameras and their dedicated UI
    for (index, (camera_name, camera_pos)) in [
        ("Player 1", Vec3::new(0.0, 200.0, -150.0)),
        ("Player 2", Vec3::new(150.0, 150., 50.0)),
        ("Player 3", Vec3::new(100.0, 150., -150.0)),
        ("Player 4", Vec3::new(-100.0, 80., 150.0)),
    ]
    .iter()
    .enumerate()
    {
        let camera = commands
            .spawn((
                Camera3d::default(),
                Transform::from_translation(*camera_pos).looking_at(Vec3::ZERO, Vec3::Y),
                Camera {
                    // Renders cameras with different priorities to prevent ambiguities
                    order: index as isize,
                    ..default()
                },
                CameraPosition {
                    pos: UVec2::new((index % 2) as u32, (index / 2) as u32),
                },
                RenderLayers::layer(index+1)
            ))
            .id();

        // Set up UI
        commands
            .spawn((
                UiTargetCamera(camera),
                Node {
                    width: Val::Percent(100.),
                    height: Val::Percent(100.),
                    ..default()
                },
            ))
            .with_children(|parent| {
                parent.spawn((
                    Text::new(*camera_name),
                    Node {
                        position_type: PositionType::Absolute,
                        top: Val::Px(12.),
                        left: Val::Px(12.),
                        ..default()
                    },
                ));
                buttons_panel(parent);
            });
    }

    fn buttons_panel(parent: &mut ChildSpawnerCommands) {
        parent
            .spawn(Node {
                position_type: PositionType::Absolute,
                width: Val::Percent(100.),
                height: Val::Percent(100.),
                display: Display::Flex,
                flex_direction: FlexDirection::Row,
                justify_content: JustifyContent::SpaceBetween,
                align_items: AlignItems::Center,
                padding: UiRect::all(Val::Px(20.)),
                ..default()
            })
            .with_children(|parent| {
                rotate_button(parent, "<", Direction::Left);
                rotate_button(parent, ">", Direction::Right);
            });
    }

    fn rotate_button(parent: &mut ChildSpawnerCommands, caption: &str, direction: Direction) {
        parent
            .spawn((
                RotateCamera(direction),
                Button,
                Node {
                    width: Val::Px(40.),
                    height: Val::Px(40.),
                    border: UiRect::all(Val::Px(2.)),
                    justify_content: JustifyContent::Center,
                    align_items: AlignItems::Center,
                    ..default()
                },
                BorderColor(Color::WHITE),
                BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
            ))
            .with_children(|parent| {
                parent.spawn(Text::new(caption));
            });
    }
}

#[derive(Component)]
struct CameraPosition {
    pos: UVec2,
}

#[derive(Component)]
struct RotateCamera(Direction);

enum Direction {
    Left,
    Right,
}

fn set_camera_viewports(
    windows: Query<&Window>,
    mut resize_events: EventReader<WindowResized>,
    mut query: Query<(&CameraPosition, &mut Camera)>,
) {
    // We need to dynamically resize the camera's viewports whenever the window size changes
    // so then each camera always takes up half the screen.
    // A resize_event is sent when the window is first created, allowing us to reuse this system for initial setup.
    for resize_event in resize_events.read() {
        let window = windows.get(resize_event.window).unwrap();
        let size = window.physical_size() / 2;

        for (camera_position, mut camera) in &mut query {
            camera.viewport = Some(Viewport {
                physical_position: camera_position.pos * size,
                physical_size: size,
                ..default()
            });
        }
    }
}

fn button_system(
    interaction_query: Query<
        (&Interaction, &ComputedNodeTarget, &RotateCamera),
        (Changed<Interaction>, With<Button>),
    >,
    mut camera_query: Query<&mut Transform, With<Camera>>,
) {
    for (interaction, computed_target, RotateCamera(direction)) in &interaction_query {
        if let Interaction::Pressed = *interaction {
            // Since TargetCamera propagates to the children, we can use it to find
            // which side of the screen the button is on.
            if let Some(mut camera_transform) = computed_target
                .camera()
                .and_then(|camera| camera_query.get_mut(camera).ok())
            {
                let angle = match direction {
                    Direction::Left => -0.1,
                    Direction::Right => 0.1,
                };
                camera_transform.rotate_around(Vec3::ZERO, Quat::from_axis_angle(Vec3::Y, angle));
            }
        }
    }
}








use std::marker::PhantomData;

use bevy::{
    app::{App, Plugin, Update},
    ecs::query::QueryFilter,
    prelude::{
        Changed, Children, Commands, Component, Entity, Local, Query,
        RemovedComponents, SystemSet, With, Without,
    },
};

/// Causes the inner component to be added to this entity and all children.
/// A child with a Propagate<C> component of it's own will override propagation from
/// that point in the tree
#[derive(Component, Clone, PartialEq)]
pub struct Propagate<C: Component + Clone + PartialEq>(pub C);

/// Internal struct for managing propagation
#[derive(Component, Clone, PartialEq)]
pub struct Inherited<C: Component + Clone + PartialEq>(pub C);

/// Stops the output component being added to this entity.
/// Children will still inherit the component from this entity or its parents
#[derive(Component, Default)]
pub struct PropagateOver<C: Component + Clone + PartialEq>(PhantomData<fn() -> C>);

/// Stops the propagation at this entity. Children will not inherit the component.
#[derive(Component, Default)]
pub struct PropagateStop<C: Component + Clone + PartialEq>(PhantomData<fn() -> C>);

pub struct HierarchyPropagatePlugin<C: Component + Clone + PartialEq, F: QueryFilter = ()> {
    _p: PhantomData<fn() -> (C, F)>,
}

impl<C: Component + Clone + PartialEq, F: QueryFilter> Default for HierarchyPropagatePlugin<C, F> {
    fn default() -> Self {
        Self {
            _p: Default::default(),
        }
    }
}

#[derive(SystemSet, Clone, PartialEq, PartialOrd, Ord)]
pub struct PropagateSet<C: Component + Clone + PartialEq> {
    _p: PhantomData<fn() -> C>,
}

impl<C: Component + Clone + PartialEq> std::fmt::Debug for PropagateSet<C> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("PropagateSet")
            .field("_p", &self._p)
            .finish()
    }
}

impl<C: Component + Clone + PartialEq> Eq for PropagateSet<C> {}
impl<C: Component + Clone + PartialEq> std:#️⃣:Hash for PropagateSet<C> {
    fn hash<H: std:#️⃣:Hasher>(&self, state: &mut H) {
        self._p.hash(state);
    }
}

impl<C: Component + Clone + PartialEq> Default for PropagateSet<C> {
    fn default() -> Self {
        Self {
            _p: Default::default(),
        }
    }
}

impl<C: Component + Clone + PartialEq, F: QueryFilter + 'static> Plugin
    for HierarchyPropagatePlugin<C, F>
{
    fn build(&self, app: &mut App) {
        app.add_systems(
            Update,
            (
                update_source::<C, F>,
                update_stopped::<C, F>,
                update_reparented::<C, F>,
                propagate_inherited::<C, F>,
                propagate_output::<C, F>,
            )
                .chain()
                .in_set(PropagateSet::<C>::default()),
        );
    }
}

pub fn update_source<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    changed: Query<(Entity, &Propagate<C>), (Changed<Propagate<C>>, Without<PropagateStop<C>>)>,
    mut removed: RemovedComponents<Propagate<C>>,
) {
    for (entity, source) in &changed {
        commands
            .entity(entity)
            .try_insert(Inherited(source.0.clone()));
    }

    for removed in removed.read() {
        if let Ok(mut commands) = commands.get_entity(removed) {
            commands.remove::<(Inherited<C>, C)>();
        }
    }
}

pub fn update_stopped<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    q: Query<Entity, (With<Inherited<C>>, F, With<PropagateStop<C>>)>,
) {
    for entity in q.iter() {
        let mut cmds = commands.entity(entity);
        cmds.remove::<Inherited<C>>();
    }
}

pub fn update_reparented<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    moved: Query<
        (Entity, &ChildOf, Option<&Inherited<C>>),
        (
            Changed<ChildOf>,
            Without<Propagate<C>>,
            Without<PropagateStop<C>>,
            F,
        ),
    >,
    parents: Query<&Inherited<C>>,
) {
    for (entity, parent, maybe_inherited) in &moved {
        if let Ok(inherited) = parents.get(parent.parent()) {
            commands.entity(entity).try_insert(inherited.clone());
        } else if maybe_inherited.is_some() {
            commands.entity(entity).remove::<(Inherited<C>, C)>();
        }
    }
}

pub fn propagate_inherited<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    changed: Query<
        (&Inherited<C>, &Children),
        (Changed<Inherited<C>>, Without<PropagateStop<C>>, F),
    >,
    recurse: Query<
        (Option<&Children>, Option<&Inherited<C>>),
        (Without<Propagate<C>>, Without<PropagateStop<C>>, F),
    >,
    mut to_process: Local<Vec<(Entity, Option<Inherited<C>>)>>,
    mut removed: RemovedComponents<Inherited<C>>,
) {
    // gather changed
    for (inherited, children) in &changed {
        to_process.extend(
            children
                .iter()
                .map(|child| (child, Some(inherited.clone()))),
        );
    }

    // and removed
    for entity in removed.read() {
        if let Ok((Some(children), _)) = recurse.get(entity) {
            to_process.extend(children.iter().map(|child| (child, None)))
        }
    }

    // propagate
    while let Some((entity, maybe_inherited)) = (*to_process).pop() {
        let Ok((maybe_children, maybe_current)) = recurse.get(entity) else {
            continue;
        };

        if maybe_current == maybe_inherited.as_ref() {
            continue;
        }

        if let Some(children) = maybe_children {
            to_process.extend(
                children
                    .iter()
                    .map(|child| (child, maybe_inherited.clone())),
            );
        }

        if let Some(inherited) = maybe_inherited {
            commands.entity(entity).try_insert(inherited.clone());
        } else {
            commands.entity(entity).remove::<(Inherited<C>, C)>();
        }
    }
}

pub fn propagate_output<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    changed: Query<
        (Entity, &Inherited<C>, Option<&C>),
        (Changed<Inherited<C>>, Without<PropagateOver<C>>, F),
    >,
) {
    for (entity, inherited, maybe_current) in &changed {
        if maybe_current.is_some_and(|c| &inherited.0 == c) {
            continue;
        }

        commands.entity(entity).try_insert(inherited.0.clone());
    }
}
```
2025-05-30 19:35:55 +00:00
Manuel Brea Carreras
3902804114
Fix #19219 by moving observer triggers out of resource_scope (#19221)
# Objective

Fixes #19219 

## Solution

Instead of calling `world.commands().trigger` and
`world.commands().trigger_targets` whenever each scene is spawned, save
the `instance_id` and optional parent entity to perform all such calls
at the end. This prevents the potential flush of the world command queue
that can happen if `add_child` is called from causing the crash.

## Testing

- Did you test these changes? If so, how?
- Verified that I can no longer reproduce the bug with the instructions
at #19219.
  - Ran `bevy_scene` tests
- Visually verified that the following examples still run as expected
`many_foxes`, `scene` . (should I test any more?)
- Are there any parts that need more testing?
- Pending to run `cargo test` at the root to test that all examples
still build; I will update the PR when that's done
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - Run bevy as usual
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - N/a (tested on Linux/wayland but it shouldn't be relevant)

---
2025-05-30 19:33:47 +00:00
eugineerd
0694743d8d
Fix EntityCloner replacing required components. (#19326)
# Objective
Fix #19324

## Solution
`EntityCloner` replaces required components when filtering. This is
unexpected when comparing with the way the rest of bevy handles required
components. This PR separates required components from explicit
components when filtering in `EntityClonerBuilder`.

## Testing
Added a regression test for this case.
2025-05-30 19:28:53 +00:00
theotherphil
0ee8bf978c
Clarify Resource change detection behaviour in condition docs (#19252)
# Objective

Fixes https://github.com/bevyengine/bevy/issues/17933

## Solution

Correct "value has changed'" in docs to "value has been added or mutably
dereferenced", with a note for emphasis copied from the docs for
Changed.

## Testing

-
2025-05-30 00:47:25 +00:00
AlephCubed
6c003ef794
Loosen add_state_scoped_event trait bound. (#19401)
Fixes #18623, allowing `add_state_scoped_event` to work with computed
states.

As a side note, should state scoped events be updated to match the
recently changed [state scoped
entities](https://github.com/bevyengine/bevy/blob/main/release-content/migration-guides/rename_StateScoped.md)?
2025-05-29 21:37:53 +00:00
Coty
439c0fb973
fix reference in example usage comments (#19434)
# Objective

- Fix a reference in the example usage comments of bevy_utils
Default::default

## Solution

- Just a word change in the comments
 
## Testing

- No actual code changes to test
2025-05-29 19:12:55 +00:00
L. Borella (Villi)
59ef10562a
Allow restricting audio playback to a custom region (#19400)
# Objective

Adds the ability to restrict playback of an audio source to a certain
region in time. In other words, you can set a custom start position and
duration for the audio clip. These options are set via the
`PlaybackSettings` component, and it works on all kinds of audio
sources.

## Solution

- Added public `start_position` and `duration` fields to
`PlaybackSettings`, both of type `Option<std::time::Duration>`.
- Used rodio's `Source::skip_duration` and `Source::take_duration`
functions to implement start position and duration, respectively.
- If the audio is looping, it interacts as you might expect - the loop
will start at the start position and end after the duration.
- If the start position is None (the default value), the audio will
start from the beginning, like normal. Similarly, if the duration is
None (default), the audio source will play for as long as possible.

## Testing

I tried adding a custom start position to all the existing audio
examples to test a bunch of different audio sources and settings, and
they all worked fine. I verified that it skips the right amount of time,
and that it skips the entire audio clip if the start position is longer
than the length of the clip. All my testing was done on Fedora Linux.

Update: I did similar testing for duration, and ensured that the two
options worked together in combination and interacted well with looping
audio.

---

## Showcase

```rust
// Play a 10 second segment of a song, starting at 0:30.5
commands.spawn((
    AudioPlayer::new(song_handle),
    PlaybackSettings::LOOP
        .with_start_position(Duration::from_secs_f32(30.5))
        .with_duration(Duration::from_secs(10))
));
```
2025-05-29 18:45:37 +00:00
andristarr
fe678e1eeb
Updating mesh_picking doc to include RenderAssetUsages (#19413)
# Objective
Fixes #19102 

Updating mesh_picking doc stating that MAIN_WORLD RenderAssetUsages
needs to be on the mesh to be picked.
2025-05-29 18:44:50 +00:00
Daniel Skates
922ee480d2
Rename Position to UiPosition in bevy_ui (#19422)
# Objective

- Fixes #19418

## Solution

- Rename Position to UiPosition in bevy_ui

## Testing

- `cargo build`
- `cargo run --example gradients`
- `cargo run --example stacked_gradients`
2025-05-29 14:52:44 +00:00
andriyDev
c364710d1f
Fix the game of life example panicking if the pipeline shader isn't ready on the first frame. (#19420)
# Objective

- Due to recent changes related to #19024, the
`compute_shader_game_of_life` example panics on some machines especially
on Linux.
- This is due to us switching more shaders to embedded shaders - this
means the compute shader in this example takes more than one frame to
load.
- The panic in the example occurs if the shader fails to load by the
first frame (since the pipeline considers that an error).

## Solution

- Make the example do nothing if the shader isn't loaded yet. This has
the effect of waiting for the shader to load.

## Testing

- Tested the example on my Linux laptop.
2025-05-29 11:30:53 +00:00
Eagster
1966e44400
EntityGeneration ordering (#19421)
# Objective

Recently the `u32` `Entity::generation` was replaced with the new
`EntityGeneration` in #19121.
This made meanings a lot more clear, and prevented accidental misuse.

One common misuse was assuming that `u32`s that were greater than others
came after those others.
Wrapping makes this assumption false.
When `EntityGeneration` was created, it retained the `u32` ordering,
which was useless at best and wrong at worst.
This pr fixes the ordering implementation, so new generations are
greater than older generations.

Some users were already accounting for this ordering issue (which was
still present in 0.16 and before) by manually accessing the `u32`
representation. This made migrating difficult for avian physics; see
[here](https://discord.com/channels/691052431525675048/749335865876021248/1377431569228103780).

I am generally of the opinion that this type should be kept opaque to
prevent accidental misuse.
As we find issues like this, the functionality should be added to
`EntityGeneration` directly.

## Solution

Fix the ordering implementation through `Ord`.

Alternatively, we could keep `Ord` the same and make a `cmp_age` method,
but I think this is better, even though sorting entity ids may be
*marginally* slower now (but more correct). This is a tradeoff.

## Testing

I improved documentation for aliasing and ordering, adding some doc
tests.
2025-05-29 05:48:32 +00:00
Chris Berger
2dd0f2c280
Update example in README to have proper Rusty formatting (#19412)
Fixes #19405
2025-05-28 18:47:45 +00:00
SpecificProtagonist
a266e7e642
More uninlined_format_args fixes (#19396)
# Objective

There are several uninlined format args (seems to be in more formatting
macros and in more crates) that are not detected on stable, but are on
nightly.

## Solution

Fix them.
2025-05-28 02:35:18 +00:00
Rob Parrett
3aaadd9b54
Minor refactoring of box_shadow example (#19404)
# Objective

Minimal effort to address feedback here:
https://github.com/bevyengine/bevy/pull/19345#discussion_r2107844018
more thoroughly.

## Solution

- Remove hardcoded label string comparisons and make more use of the new
enum added during review
- Resist temptation to let this snowball this into a huge refactor
- Maybe come back later for a few other small improvements

## Testing

`cargo run --example box_shadow`
2025-05-27 23:44:32 +00:00
SpecificProtagonist
13e89a1678
Fix EntityMeta.spawned_or_despawned unsoundness (#19350)
# Objective

#19047 added an `MaybeUninit` field to `EntityMeta`, but did not
guarantee that it will be initialized before access:

```rust
let mut world = World::new();
let id = world.entities().reserve_entity();
world.flush();
world.entity(id);
```

<details>
<summary>Miri Error</summary>

```
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
    --> /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1121:26
     |
1121 |                 unsafe { meta.spawned_or_despawned.assume_init() }
     |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
     |
     = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
     = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
     = note: BACKTRACE:
     = note: inside closure at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1121:26: 1121:65
     = note: inside `std::option::Option::<&bevy_ecs::entity::EntityMeta>::map::<bevy_ecs::entity::SpawnedOrDespawned, {closure@bevy_ecs::entity::Entities::entity_get_spawned_or_despawned::{closure#1}}>` at /home/vj/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:1144:29: 1144:33
     = note: inside `bevy_ecs::entity::Entities::entity_get_spawned_or_despawned` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1112:9: 1122:15
     = note: inside closure at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1094:13: 1094:57
     = note: inside `bevy_ecs::change_detection::MaybeLocation::<std::option::Option<&std::panic::Location<'_>>>::new_with_flattened::<{closure@bevy_ecs::entity::Entities::entity_get_spawned_or_despawned_by::{closure#0}}>` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/change_detection.rs:1371:20: 1371:24
     = note: inside `bevy_ecs::entity::Entities::entity_get_spawned_or_despawned_by` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1093:9: 1096:11
     = note: inside `bevy_ecs::entity::Entities::entity_does_not_exist_error_details` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1163:23: 1163:70
     = note: inside `bevy_ecs::entity::EntityDoesNotExistError::new` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1182:22: 1182:74
     = note: inside `bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell::<'_>::get_entity` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/unsafe_world_cell.rs:368:20: 368:73
     = note: inside `<bevy_ecs::entity::Entity as bevy_ecs::world::WorldEntityFetch>::fetch_ref` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/entity_fetch.rs:207:21: 207:42
     = note: inside `bevy_ecs::world::World::get_entity::<bevy_ecs::entity::Entity>` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/mod.rs:911:18: 911:42
note: inside `main`
    --> src/main.rs:12:15
     |
12   |     world.entity(id);
     |
```

</details>

## Solution

- remove the existing `MaybeUninit` in `EntityMeta.spawned_or_despawned`
- initialize during flush. This is not needed for soundness, but not
doing this means we can't return a sensible location/tick for flushed
entities.

## Testing

Test via the snippet above (also added equivalent test).

---------

Co-authored-by: urben1680 <55257931+urben1680@users.noreply.github.com>
2025-05-27 22:45:07 +00:00
andriyDev
b866bb4254
Remove Shader weak_handles from bevy_pbr (excluding meshlets). (#19365)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_pbr` (excluding meshlets).

## Testing

- `atmosphere` example still works
- `fog` example still works
- `decal` example still works

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:47 +00:00
andriyDev
e8c2a5af66
Remove Shader weak_handles from bevy_ui. (#19393)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_ui`.

## Testing

- `box_shadow` example still works.
- `gradient` example is broken at head (see #19384) - but otherwise
gives the same result in the console.
- `ui_materials` example still works.
- `ui_texture_slice` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:40 +00:00
andriyDev
846cec9ed2
Remove Shader weak_handles from bevy_gizmos. (#19394)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_gizmos`.

## Testing

- `2d_gizmos` example still works.
- `3d_gizmos` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:32 +00:00
andriyDev
57588eb7eb
Remove Shader weak_handles from bevy_core_pipeline (except two). (#19395)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_core_pipeline`.

## Testing

- `bloom_3d` example still works.
- `motion_blur` example still works.
- `meshlet` example still works (it uses a shader from core).

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:27 +00:00
Chris Berger
a8376e982e
Rename Timer::finished and Timer::paused to is_finished and is_paused (#19386)
# Objective
Renames `Timer::finished` and `Timer::paused` to `Timer::is_finished`
and `Timer::is_paused` to align the public APIs for `Time`, `Timer`, and
`Stopwatch`.

Fixes #19110
2025-05-27 22:24:18 +00:00
Rob Parrett
a575502886
Move radial_gradients example to UI testbed (#19390)
# Objective

Fixes #19385

Note: this has shader errors due to #19383 and should probably be merged
after #19384

## Solution

- Move the example to the UI testbed
- Adjust label contents and cell size so that every test case fits on
the screen
- Minor tidying, slightly less harsh colors while preserving the
intentional debug coloring

## Testing

`cargo run --example testbed_ui`

![Screenshot 2025-05-27 at 8 53
43 AM](https://github.com/user-attachments/assets/97ea20ee-d265-45f6-8b99-bcd5f6030e30)

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-27 22:06:19 +00:00
Rob Parrett
e0ed28022d
Fix error in gradient shader (#19384)
# Objective

Fixes #19383

## Solution

Add missing param and flags from `ui.wgsl` to `gradients.wgsl`

## Testing

`cargo run --example gradients`
`cargo run --example stacked_gradients`
`cargo run --example radial_gradients`

## Notes

`radial_gradients` looks broken, but this appears to be a separate
issue. Its appearance now is the same as in the [first
screenshot](https://pixel-eagle.com/project/b25a040a-a980-4602-b90c-d480ab84076d/run/10348/compare/10342?screenshot=UI%20(User%20Interface)/radial_gradients.png)
recorded in the example runner.

I will document this in a separate issue.
2025-05-27 21:29:10 +00:00
HeartofPhos
131f99de23
Fix custom relations panics with parent/child relations (#19341)
# Objective

Fixes #18905

## Solution

`world.commands().entity(target_entity).queue(command)` calls
`commands.with_entity` without an error handler, instead queue on
`Commands` with an error handler

## Testing

Added unit test

Co-authored-by: Heart <>
2025-05-27 21:05:31 +00:00
Griffin
e981bb8902
Make light cascades and tonemapping luts pub (#19189)
Make directional light cascades and tonemapping luts pub so that custom
render passes / backends can use them.
2025-05-27 19:46:04 +00:00
oracle58
8e585174ee
box_shadow example with adjustable settings (#19345)
# Objective

- Addresses the previous example's lack of visual appeal and clarity. It
was missing labels for clear distinction of the shadow settings used on
each of the shapes. The suggestion in the linked issue was to either
just visually update and add labels or to collapse example to a single
node with adjustable settings.
- Fixes #19240

## Solution

- Replace the previous static example with a single, central node with
adjustable settings as per issue suggestion.
- Implement button-based setting adjustments. Unfortunately slider
widgets don't seem available yet and I didn't want to further bloat the
example.
- Improve overall aesthetics of the example -- although color pallette
could still be improved. flat gray tones are probably not the best
choice as a contrast to the shadow, but the white border does help in
that aspect.
- Dynamically recolor shadows for visual clarity when increasing shadow
count.
- Add Adjustable Settings:
    - Shape selection
    - Shadow X/Y offset, blur, spread, and count
- Add Reset button to restore default settings

The disadvantage of this solution is that the old example code would
have probably been easier to digest as the new example is quite bloated
in comparison. Alternatively I could also just implement labels and fix
aesthetics of the old example without adding functionality for
adjustable settings, _but_ I personally feel like interactive examples
are more engaging to users.

## Testing

- Did you test these changes? If so, how? `cargo run --example
box_shadow` and functionality of all features of the example.
- Are there any parts that need more testing? Not that I am aware of. 
- How can other people (reviewers) test your changes? Is there anything
specific they need to know? Not really, it should be pretty
straightforward just running the new example and testing the feats.

---

## Showcase

![box-shadow-example-1](https://github.com/user-attachments/assets/57586b30-c290-4e3f-9355-5c3f6e9a6406)


![box-shadow-example-2](https://github.com/user-attachments/assets/51a51d2f-dd30-465b-b802-ddb8077adff5)

---------

Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
2025-05-27 19:43:57 +00:00
Chris Russell
571b3ba475
Remove ArchetypeComponentId and archetype_component_access (#19143)
# Objective

Remove `ArchetypeComponentId` and `archetype_component_access`.
Following #16885, they are no longer used by the engine, so we can stop
spending time calculating them or space storing them.

## Solution

Remove `ArchetypeComponentId` and everything that touches it.  

The `System::update_archetype_component_access` method no longer needs
to update `archetype_component_access`. We do still need to update query
caches, but we no longer need to do so *before* running the system. We'd
have to touch every caller anyway if we gave the method a better name,
so just remove `System::update_archetype_component_access` and
`SystemParam::new_archetype` entirely, and update the query cache in
`Query::get_param`.

The `Single` and `Populated` params also need their query caches updated
in `SystemParam::validate_param`, so change `validate_param` to take
`&mut Self::State` instead of `&Self::State`.
2025-05-27 19:04:32 +00:00
atlv
2946de4573
doc(render): fix incorrectly transposed view matrix docs (#19317)
# Objective

- Mend incorrect docs

## Solution

- Mend them
- add example use
- clarify column major

## Testing

- No code changes
2025-05-27 04:58:58 +00:00
atlv
d4985af7cb
refactor(utils): move SyncCell and SyncUnsafeCell to bevy_platform (#19305)
# Objective

- move SyncCell and SyncUnsafeCell to bevy_platform

## Solution

- move SyncCell and SyncUnsafeCell to bevy_platform

## Testing

- cargo clippy works
2025-05-27 04:57:26 +00:00
andriyDev
dc6563477d
Remove Shader weak_handles from bevy_sprite. (#19392)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_sprite`.

## Testing

- `sprite` example still works.
- `mesh2d` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 04:01:34 +00:00
atlv
1732c2253b
refactor(render): move WgpuWrapper into bevy_utils (#19303)
# Objective

- A step towards splitting out bevy_camera from bevy_render

## Solution

- Move a shim type into bevy_utils to avoid a dependency cycle
- Manually expand Deref/DerefMut to avoid having a bevy_derive
dependency so early in the dep tree

## Testing

- It compiles
2025-05-27 03:43:49 +00:00
AlephCubed
7d32dfec18
Add insert_if_new test for sparse set. (#19387)
Fixes #19081.
Simply created a duplicate of the existing `insert_if_new` test, but
using sparse sets.

## Testing:
The test passes on main, but fails if #19059 is reverted.
2025-05-27 03:15:30 +00:00
andriyDev
33b3c08515
Remove Shader weak_handles from bevy_anti_aliasing. (#19391)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_anti_aliasing`.

## Testing

- `anti_aliasing` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 03:14:55 +00:00
Rob Parrett
0ff44c9493
Small fixes for gradient docs (#19388)
# Objective

Found a typo while looking at gradients in another issue and gave the
docs a skim for more.

## Solution

A couple typo fixes and some tiny improvements
2025-05-26 23:20:24 +00:00
theotherphil
1e1853b876
Add missing doc comments for log_diagnostics_plugin (#19261)
# Objective

Fill some of the missing docs from bevy_diagnostics.
2025-05-26 22:29:59 +00:00
extrawurst
0a11091ea8
Adding context menu example (#19245)
# Objective

Provides usage example of a context menu

## Testing

* [x] Tested on MacOS
* [x] Tested on wasm using bevy_cli

---

## Showcase



https://github.com/user-attachments/assets/2e39cd32-131e-4535-beb7-b46680bca74a

---------

Co-authored-by: Rob Parrett <robparrett@gmail.com>
2025-05-26 22:27:19 +00:00
ickshonpe
f415e2e96d
Constify Val::resolve and BorderRadius::resolve (#18595)
# Objective

Constify `Val::resolve` and  `BorderRadius::resolve`

# Solution

* Replace uses of `Vec2::min_element` and `Vec2::max_element` with `min`
and `max` called on the components.
* Make `BorderRadius::resolve` and `BorderRadius::resolve_single_corner`
`const`.
* Swap the order of the `bottom_left` and `bottom_right` fields of
`BorderRadius` and `ResolvedBorderRadius` so they match the ccw order
used in the shader and in css.
2025-05-26 22:17:52 +00:00
Rob Parrett
2e37783242
Move cooldown example instruction text according to example visual guidelines (#19381)
# Objective

Use the same text positioning as other examples that have instruction
text.

See
https://bevyengine.org/learn/contribute/helping-out/creating-examples/#visual-guidelines
2025-05-26 22:01:08 +00:00
theotherphil
523600133d
Add missing docs for diagnostics.rs (#19264)
# Objective

Fill in some more missing doc comments.

---------

Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2025-05-26 20:27:46 +00:00
theotherphil
16a286dac3
Update .entry() docs to show both insert-then-modify and modify-or-insert examples (#19327)
# Objective

Fix https://github.com/bevyengine/bevy/issues/16379
2025-05-26 20:27:20 +00:00
François Mockers
8a223be651
Enable state scoped entities by default (#19354)
# Objective

- Enable state scoped entities by default
- Provide a way to disable it when needed

---------

Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
2025-05-26 20:26:41 +00:00
andriyDev
8db7b6e122
Remove Shader weak_handles from bevy_render. (#19362)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
bevy_render.

## Testing

- `animate_shader` example still works

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-26 20:20:25 +00:00
ickshonpe
f04c0ef689
tab_navigation example improvements (#19239)
# Objective

Improve the `tab_navigation` example.

## Solution

* Set different `TabIndex`s for the buttons of each group.
* Label each button with its associated `TabIndex`.
* Reduce the code duplication using a loop.

I tried to flatten it further using the new spawning APIs and
`children!` macro but not sure what the current best way to attach the
observers is.
2025-05-26 20:20:00 +00:00
SpecificProtagonist
158d9aff0e
Fix spawn tracking for spawn commands (#19351)
# Objective

See also
https://discord.com/channels/691052431525675048/1374187654425481266/1375553989185372292.

## Solution

Set spawn info in `Commands::spawn_empty`.
Also added a benchmark for `Commands::spawn`.

## Testing

See added test.
2025-05-26 20:15:21 +00:00
theotherphil
54c9f03021
Mention in .add_observer() docs that first parameter must be a Trigger (#19315)
# Objective

Fix https://github.com/bevyengine/bevy/issues/13860

## Solution

Add note in docs that Trigger must be the first parameter of observer
systems
2025-05-26 20:06:08 +00:00
Benjamin Brienen
818a8fe154
Allow unfocused window creation (#19237)
# Objective

Allow creating a new window without it being focused, when `Window`'s
`focused` is `false.
## Solution

Use `winit`'s `WindowBuilder`'s `with_active` method

## Notes

- `winit`'s doc lists [redox's
`Orbital`](https://gitlab.redox-os.org/redox-os/orbital) as an
unsupported platform, but since Bevy doesn't officially support this
platform, I didn't put it in the documentation.

- I only tested on Linux, which is an unsupported platform. I can give
you a test code if you want to test on another platform.

- I initially put a line
[here](https://github.com/bevyengine/bevy/blob/v0.11.0/crates/bevy_winit/src/system.rs#L72)
to set the Bevy `Window`'s `focused` to `winit_window.has_focus()` after
window creation to avoid the case where `with_active` is not supported,
the window is spawned focused, no `WindowFocused` event is triggered,
and Bevy `Window` would be desynced from winit's window. But after
testing on Linux (which doesn't support `with_active`) it seems like at
that point `has_focus` returns `false` and the event is triggered, so I
removed it. Do you think I should add it back to be safe?

## Changelog

- A new unfocused `Window` can be created by setting `focused` to
`false`.

## Migration Guide

- If a `Window` is spawned with `focused` set to `false`, it will now
start not focused on supported platforms.

Adopted from #9208

---------

Co-authored-by: Sélène Amanita <selene.amanita@net-c.com>
Co-authored-by: Sélène Amanita <134181069+Selene-Amanita@users.noreply.github.com>
Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-26 20:05:33 +00:00
theotherphil
f8cb8f237d
Fix a few typos in bevy_ecs docs (#19280)
# Objective

Fix a few minor typos that I noticed when reading the docs.
2025-05-26 20:02:13 +00:00
Niklas Eicker
7c4a1f9d87
Unpin nightly in CI (#19278)
# Objective

In #19253 we pinned nightly due to a bug in Miri. That issue was
resolved and the latest Miri should be usable for us again.

## Solution

- Use latest nightly again

## Testing

- I tested today's Miri locally with
`RUSTUP_TOOLCHAIN=nightly-2025-05-18 MIRIFLAGS="-Zmiri-ignore-leaks
-Zmiri-disable-isolation" RUSTFLAGS="-Zrandomize-layout" cargo miri test
-p bevy_ecs`
2025-05-26 20:01:10 +00:00
theotherphil
348779850b
Add missing doc comments to system_information_diagnostics_plugin (#19267)
# Objective

More trivial doc comments towards being able to deny missing docs across
the board.
2025-05-26 19:59:49 +00:00
theotherphil
3690ad5b0b
Remove apostrophes in possessive its (#19244)
# Objective

Fix some grammatical errors: it's -> its

Not the most useful commit in the world, but I saw a couple of these and
decided to fix the lot.

## Solution
-

## Testing
-
2025-05-26 19:53:14 +00:00
theotherphil
07c9d1acce
Clarify RenderLayers docs (#19241)
# Objective

Clarify `RenderLayers` docs, to fix
https://github.com/bevyengine/bevy/issues/18874

## Solution

-

## Testing

-
2025-05-26 19:52:22 +00:00
theotherphil
a521998eea
Make remove_reflect parameter names consistent between ReflectCommandExt and impl for EntityCommands (#19243)
# Objective

Fix https://github.com/bevyengine/bevy/issues/13390

## Solution

The second parameter of the remove_reflect function is called
component_type_name in ReflectCommandExt but component_type_path in the
implementation for EntityCommands. Use component_type_path in both
places.

## Testing

None
2025-05-26 19:49:56 +00:00
Niklas Eicker
923c2ad281
New cooldown example (#19234)
# Objective

We want to extend our examples with a new category "usage" to
demonstrate common use cases (see bevyengine/bevy-website#2131). This PR
adds an example of animated cooldowns on button clicks.

## Solution

- New example in "usage" directory
- Implement a cooldown with an animated child Node

## Testing

- I ran this on Linux
- [x] test web (with bevy CLI: `bevy run --example cooldown web --open`)

---------

Co-authored-by: Thierry Berger <contact@thierryberger.com>
Co-authored-by: Ida "Iyes" <40234599+inodentry@users.noreply.github.com>
2025-05-26 19:44:17 +00:00
Benjamin Brienen
20057e5ed7
Make sure that serde_json::Map::into_values exists (#19229)
# Objective

cargo update was required to build because into_values was added in a
patch version

## Solution

Depend on the new patch

## Testing

Builds locally now
2025-05-26 19:38:28 +00:00
stevehello166
66877f63be
Add a Dir4 to resolve #17983 (#19223)
# Objective
Fixes #17983 

## Solution

Implemented a basic `Dir4` struct with methods similar to `Dir3` and
`Dir2`.

## Testing

- Did you test these changes? If so, how?
Added unit tests that follow the same pattern of the other Dir structs.
- Are there any parts that need more testing?
Since the other Dir structs use rotations to test renormalization and I
have been following them as a pattern for the Dir4 struct I haven't
implemented a test to cover renormalization of Dir4's.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
Use Dir4 in the wild.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
N/A (Tested on Linux/X11 but it shouldn't be a problem)
2025-05-26 19:35:07 +00:00
Chris Berger
0777cc26aa
Navigate through TabGroups in order. (#19218)
# Objective
Fixes #19156

## Solution
There was a call to sort by `TabIndex` after iterating through all the
`TabGroup`s. Simply removing that line of code made the new test case
pass (navigating through `TabIndex` within different `TabGroup`s and
kept everything else working as expected as far as I can tell.

I went ahead and broke the `focusable` vec down into per-group vecs that
get sorted by `TabIndex` and appended to the main vec in group sorted
order to maintain the sorting that was being done before, but respecting
the `TabGroup` sorting that was missing in the issue that this PR
addresses.

## Testing
I added a new unit test that reproduced the issue outlined above that
will run in CI. This test was failing before deleting the sort, and now
both unit tests are passing.
2025-05-26 19:32:10 +00:00
AlephCubed
0f92d1087c
Fixed broken link in release content README. (#19375)
The link went to a 404 page because it was missing an 's'.
2025-05-26 19:30:03 +00:00
urben1680
b92c0ebd3d
No schedule build pass overwrite if build settings do not change auto_insert_apply_deferred from true (#19217)
# Objective

Fixes #18790.
Simpler alternative to #19195.

## Solution

As suggested by @PixelDust22, simply avoid overwriting the pass if the
schedule already has auto sync points enabled.
Leave pass logic untouched.

It still is probably a bad idea to add systems/set configs before
changing the build settings, but that is not important as long there are
no more complex build passes.

## Testing

Added a test.

---------

Co-authored-by: Thierry Berger <contact@thierryberger.com>
2025-05-26 19:28:56 +00:00
IceSentry
2dc6337875
Add WakeUp event to App (#19212)
# Objective

- The WakeUp event is never added to the app. If you need to use that
event you currently need to add it yourself.

## Solution

- Add the WakeUp event to the App in the WinitPlugin

## Testing

- I tested the window_setting example and it compiled and worked
2025-05-26 19:26:26 +00:00
Emerson Coskey
7ab00ca185
Split Camera.hdr out into a new component (#18873)
# Objective

- Simplify `Camera` initialization
- allow effects to require HDR

## Solution

- Split out `Camera.hdr` into a marker `Hdr` component

## Testing

- ran `bloom_3d` example

---

## Showcase

```rs
// before
commands.spawn((
  Camera3d
  Camera {
    hdr: true
    ..Default::default()
  }
))

// after
commands.spawn((Camera3d, Hdr));

// other rendering components can require that the camera enables hdr!
// currently implemented for Bloom, AutoExposure, and Atmosphere.
#[require(Hdr)]
pub struct Bloom;
```
2025-05-26 19:24:45 +00:00
Fallible Things
cedf8d357a
Explanation for the '2d shapes' example (#19211)
[Explanation](https://bevyengine.org/learn/contribute/helping-out/explaining-examples/)
for the 2d shapes example, taken from the original HackMD document and
edited a bit.

This example is a strange one, it's eye-catching mostly because it's the
first example (at time of writing) in the examples page. That being
said, the example does a decent amount of teaching utility to it: we can
explain the bevy math-shape to mesh pipeline, which illuminates a way of
transforming one form of data (abstract, mathematical shape
descriptions) into another (meshes) which may be novel or inspirational
to some users.

---------

Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
2025-05-26 19:21:01 +00:00
Gino Valente
1674938c49
bevy_reflect: Split up the std module (#18939)
> [!important]
> To **maintainers**: we should wait to merge this one in until after
#18944 lands so that cherry-picking the latter for 0.16.1 is simpler.

# Objective

The `std` module is where we implement the reflection traits for types
that are exported from `std` (including `core` and `alloc`). Over time,
this file has grown increasingly large, making it difficult to navigate
and a pain point for merge conflicts.

The goal of this PR is to break up the module into smaller chunks.

## Solution

The `std` module has been split into many submodules:
- `alloc`
- `bevy_platform`
- `core`
- `std`

Each of these new modules is comprised of submodules that closely
resemble the actual module. For example, the impls for
`::alloc::vec::Vec` have been moved to
`bevy_reflect::impls::alloc::vec::Vec`.

Some liberties were taken. For example, `Cow<'static, Path>` was kept in
`bevy_reflect::impls::std::path` rather than
`bevy_reflect::impls::alloc::borrow`.

You may ask: _Isn't this a little overkill? Why does the one-line impl
for `TypeId` need its own file?_

And yes, it is partly overkill. But the benefit with this approach is
that where an `std`-related type should live is mostly unambiguous. If
we wanted to reflect `::core::net::Ipv4Addr`, it's very clear that it
should be done in `bevy_reflect::impls::core::net`.

We can discuss better ways of breaking this up if people have other
ideas or opinions, but I think this is a pretty straightforward way of
doing it.

### Note to Reviewers

The code is pretty much copy-paste from the mega module to the new
submodules. It's probably best to focus efforts on reviewing the general
module structure, as well as maybe which impls are included where.

You _can_ review the code contained within each impl, but I promise you
the only thing I touched were the paths in the macros so they could be
more hygienic :)

## Testing

You can just check that everything compiles still:

```
cargo check -p bevy_reflect --tests
```
2025-05-26 19:10:47 +00:00
IceSentry
85284cbb90
Move trigger_screenshots to finish() (#19204)
# Objective

- The tigger_screenshots system gets added in `.build()` but relies on a
resource that is only inserted in `.finish()`
- This isn't a bug for most users, but when doing headless mode testing
it can technically work without ever calling `.finish()` and did work
before bevy 0.15 but while migrating my work codebase I had an issue of
test failing because of this

## Solution

- Move the trigger_screenshots system to `.finish()`

## Testing

- I ran the screenshot example and it worked as expected
2025-05-26 18:07:17 +00:00
Griffin
d79efada3b
Optional explicit compressed image format support (#19190)
# Objective

- Allow compressed image formats to be used with `ImagePlugin` and
`GltfPlugin` in cases where there is no `RenderDevice` resource. (For
example, when using a custom render backend)

## Solution

- Define a `CompressedImageFormatSupport` component that allows the user
to explicitly determine which formats are supported.

~~Not sure if this is the best solution. Alternatively, I considered
initializing CompressedImageFormatSupport from render device features
separately, it would need to run after the render device is initialized
but before `ImagePlugin` and `GltfPlugin` finish. Not sure where the
best place for that to happen would be.~~

Update: decided on going with @greeble-dev solution: defining the
`CompressedImageFormatSupport` resource in `bevy_image`, but letting
`bevy_render` register the resource value.
2025-05-26 18:00:33 +00:00
universe
5117e6fd49
don't filter dragged entity out of DragEnter events (#19179)
## produce a DragEnter event when reentering the dragged entity

when making a piano, i want dragging across the keys to trigger the
notes of each key, but currently if i drag out of a key, then back to
it, this will not work since the dragged entity gets filtered out

## Solution

- make DragEnter event work whenever there's an entry. if the user wants
to ignore the dragged entity they can compare `target` and `dragged`

## Testing

- tested this with a modified version of the 2d_shapes example. i added
an observer to the entities: (and added mesh picking plugin)
```rust
.observe(|t: Trigger<Pointer<DragEnter>>| {
    info!("entered {}, started from {}", t.target(), t.dragged);
}
```
- i'm not sure if other things need more testing, or if this is wrong
completely and breaks other things i don't know of!

---

## Showcase

before:


https://github.com/user-attachments/assets/48de606a-e44d-4ca1-ae16-d8dcef640d6e

after:


https://github.com/user-attachments/assets/b1be231f-c826-47bc-be43-c637f22e7846
2025-05-26 17:56:54 +00:00
Stepan Urazov
53f1c06e63
Added support for .wesl files to the regex pattern for examples (#19178)
## Objective

[Shaders / Material -
WESL](https://bevyengine.org/examples-webgpu/shaders/shader-material-wesl/)
example doesn't have a WESL file tab


## Solution

 Added wesl to regex

---------

Co-authored-by: Stepan Urazov <110625288+hg127@users.noreply.github.com>
2025-05-26 17:52:59 +00:00
Lomírus
d1f6470cc2
Fix mismatched FogFalloff (#19174)
# Objective

When user presses <kbd>3</kbd>, the falloff mode should be changed to
`ExponentialSquared` as described in the instructions, but it's not in
fact.

Online Example: https://bevyengine.org/examples-webgpu/3d-rendering/fog/

## Solution

Change it to `ExponentialSquared`

## Testing

- Did you test these changes? If so, how?

Yes, by `cargo run --example fog`

- Are there any parts that need more testing?

No.

- How can other people (reviewers) test your changes? Is there anything
specific they need to know?

```
cargo run --example fog
```

- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?

N/A
2025-05-26 17:52:27 +00:00
Weihnachtsbaum
841105a993
Add AudioSinkPlayback::position (#19173)
# Objective

- Allow users to get the playback position of playing audio.

## Solution

- Add a `position` method to `AudioSinkPlayback`
- Implement it for `AudioSink` and `SpatialAudioSink`

## Testing

- Updated `audio_control` example to show playback position
2025-05-26 17:50:40 +00:00
Arnold Loubriat
645871e74e
Bump accesskit to 0.19 and accesskit_winit to 0.27 (#19160)
# Objective

- Update AccessKit crates to their latest versions.
- Fixes #19040 

## Solution

- Only modifying Cargo.toml files is needed, few changes under the hood
but nothing impacting Bevy.

## Testing

- I ran the tab_navigation example on Windows 11.
2025-05-26 17:48:36 +00:00
Eagster
b6b54912fa
Nonmax all rows (#19132)
# Objective

Since #18704 is done, we can track the length of unique entity row
collections with only a `u32` and identify an index within that
collection with only a `NonMaxU32`. This leaves an opportunity for
performance improvements.

## Solution

- Use `EntityRow` in sparse sets.
- Change table, entity, and query lengths to be `u32` instead of
`usize`.
- Keep `batching` module `usize` based since that is reused for events,
which may exceed `u32::MAX`.
- Change according `Range<usize>` to `Range<u32>`. This is more
efficient and helps justify safety.
- Change `ArchetypeRow` and `TableRow` to wrap `NonMaxU32` instead of
`u32`.

Justifying `NonMaxU32::new_unchecked` everywhere is predicated on this
safety comment in `Entities::set`: "`location` must be valid for the
entity at `index` or immediately made valid afterwards before handing
control to unknown code." This ensures no entity is in two table rows
for example. That fact is used to argue uniqueness of the entity rows in
each table, archetype, sparse set, query, etc. So if there's no
duplicates, and a maximum total entities of `u32::MAX` none of the
corresponding row ids / indexes can exceed `NonMaxU32`.

## Testing

CI

---------

Co-authored-by: Christian Hughes <9044780+ItsDoot@users.noreply.github.com>
2025-05-26 17:39:55 +00:00
atlv
7bb97a7eec
Provide physical dimensions to wgpu Device::create_texture_with_data (#19129)
# Objective

- Make errors in #19124 #13289 clearer, and opt for option 1. of
https://github.com/gfx-rs/wgpu/issues/7677

## Solution

Remove the round to block size `.physical_size(texture_format);`

Error message now becomes much clearer:
```
thread 'Compute Task Pool (5)' panicked at E:\r\wgpu\wgpu\src\backend\wgpu_core.rs:1423:26:
wgpu error: Validation Error

Caused by:
  In Device::create_texture
    Width 2050 is not a multiple of Bc7RgbaUnormSrgb's block width (4)
```

## Testing

- Tested using the repro in #19124
2025-05-26 17:36:47 +00:00
Gilles Henaux
6341ae4158
Fix AssetChanged code documentation to mention the PostUpdate schedule (#19093)
# Objective

- Fix `AssetChanged` code documentation to mention the `PostUpdate`
schedule instead of the `Last` schedule

## Testing

- Trivial (code doc). Check `bevy_asset/src/lib.rs` in function
`init_asset` to see where this is scheduled:
```rust
           .add_systems(
                PostUpdate,
                Assets::<A>::asset_events
                    .run_if(Assets::<A>::asset_events_condition)
                    .in_set(AssetEvents),
            )
```

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-26 17:34:46 +00:00
SpecificProtagonist
57eda2efa1
Remove warning from Image::resize (#19116)
# Objective

`Image::resize` currently prints a warning when resizing an
uninitialized `Image`, even though the resizing works correctly. For
[code](c92f14c9e7/crates/bevy_ui/src/widget/viewport.rs (L175-L179))
that doesn't care about whether the image is initialized CP-side, this
is inconvenient and unnecessary.
2025-05-26 17:34:07 +00:00
noxmore
c84a808f80
Derive PartialEq for ImageSampler (#19105)
# Objective

Ran into a situation where I need to compare two image samplers. My
current workaround is to compare the `Debug` outputs

## Solution

Derive `PartialEq` on `ImageSampler` and structs in its fields.

## Testing

Full CI passed.
2025-05-26 17:31:21 +00:00
Kumikaya
ee3d9b2a88
Reduce operations in RayCast2d::circle_intersection_at using cross product (#19103)
# Objective

- Using the cross product to compute the perpendicular distance reduces
one multiplication and two additions.
2025-05-26 17:22:14 +00:00
SpecificProtagonist
1c8d2ee3e1
viewport_node example: Remove main world image initialization (#19098)
# Objective

The new viewport example allocates a texture in main memory, even though
it's only needed on the GPU. Also fix an unnecessary warning when a
viewport's texture doesn't exist CPU-side.

## Testing

Run the `viewport_node` example.
2025-05-26 17:20:29 +00:00
robtfm
b641aa0ecf
separate border colors (#18682)
# Objective

allow specifying the left/top/right/bottom border colors separately for
ui elements

fixes #14773

## Solution

- change `BorderColor` to 
```rs
pub struct BorderColor {
    pub left: Color,
    pub top: Color,
    pub right: Color,
    pub bottom: Color,
}
```
- generate one ui node per distinct border color, set flags for the
active borders
- render only the active borders

i chose to do this rather than adding multiple colors to the
ExtractedUiNode in order to minimize the impact for the common case
where all border colors are the same.

## Testing

modified the `borders` example to use separate colors:


![image](https://github.com/user-attachments/assets/5d9a4492-429a-4ee1-9656-215511886164)

the behaviour is a bit weird but it mirrors html/css border behaviour.

---

## Migration:

To keep the existing behaviour, just change `BorderColor(color)` into
`BorderColor::all(color)`.

---------

Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
2025-05-26 16:57:13 +00:00
Emerson Coskey
fb2d79ad60
Better macro errors for get_struct_fields (#17639)
# Objective

- Currently, the error span for `get_struct_field` when encountering an
enum or union points to the macro invocation, rather than the `enum` or
`union` token. It also doesn't mention which macro reported the error.

## Solution

- Report the correct error span
- Add parameter for passing in the name of the macro invocation

## Testing

Bevy compiles fine with this change

## Migration Guide

```rs
// before
let fields = get_struct_fields(&ast.data);

// after
let fields = get_struct_fields(&ast.data, "derive(Bundle)");
```

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-05-26 16:57:03 +00:00
Periwink
f33074116b
Make SystemChangeTick Clone (#18991)
# Objective

It can be useful to store the `SystemChangeTick` for a given System. I
wanted to copy them but I noticed that it doesn't implement Clone.
2025-05-26 15:43:06 +00:00
Ben Frankel
475b5a8557
Improve visibility of debug picking node (#18990)
# Objective

Fixes https://github.com/bevyengine/bevy/issues/18989.

## Solution

Add `GlobalZIndex(i32::MAX)`,
`BackgroundColor(Color::BLACK.with_alpha(0.75))`, and some padding.

## Testing

Ran `cargo run --example debug_picking`:


![2025-04-30_1745997924_1280x720](https://github.com/user-attachments/assets/4bd39897-f4ab-4d4d-850b-1b885284b072)

Before this PR:


![2025-04-29_1745972540_1280x720](https://github.com/user-attachments/assets/9c8649f4-0f06-4d4d-8fed-ac20e0d5366e)
2025-05-26 15:39:49 +00:00
Tim
4924cf5828
Remove upcasting methods + Cleanup interned label code (#18984)
Hiya!

# Objective

- Remove upcasting methods that are no longer necessary since Rust 1.86.
- Cleanup the interned label code.
 
## Notes
- I didn't try to remove the upcasting methods from `bevy_reflect`, as
there appears to be some complexity related to remote type reflection.
- There are likely some other upcasting methods floating around.

## Testing
I ran the `breakout` example to check that the hashing/eq
implementations of the labels are still correct.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-26 15:38:12 +00:00
Jakob Hellermann
93b8f9a303
bevy_ecs: forward type_id in InfallibleSystemWrapper (#18931)
similar to https://github.com/bevyengine/bevy/pull/12030

# Objective

`bevy_mod_debugdump` uses the `SystemTypeSet::system_type` to look up
constrains like `(system_1, system_2.after(system_1))`. For that it
needs to find the type id in `schedule.graph().systems()`

Now with systems being wrapped in an `InfallibleSystemWrapper` this
association was no longer possible.

## Solution

By forwarding the type id in `InfallibleSystemWrapper`,
`bevy_mod_debugdump` can resolve the dependencies as before, and the
wrapper is an unnoticable implementation detail.

## Testing

- `cargo test -p bevy_ecs`
I'm not sure what exactly could break otherwise.
2025-05-26 15:33:05 +00:00
Saladin
af874a58c3
Added explicit dependencies for Arch Linux (#10386)
As I run bevy in a apx container, the default dependencies are bare
minimum.. because of which I was able to find the explicit dependencies
required for bevy. 👍 the same shall also be applicable for the other
distributions but I will have to figure out explicit ones for them and
might open a PR for them later.

# Objective

- Have explicit dependencies listed for a bare minimum system /
container

## Solution

- Running bevy in a bare minimum apx / distrobox container.

---

## Changelog

- Added explicit dependency instructions for Arch Linux

Co-authored-by: SHuRiKeN <40650341+shuriken1812@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-26 15:32:48 +00:00
Sébastien Job
2b8bf45f0d
Fix BRP query failing when specifying missing/invalid components (#18871)
# Objective

- Fixes #18869.

## Solution

The issue was the `?` after a `Result` raising the error, instead of
treating it.
Instead it is handled with `ok`, `and_then`, `map` ...

_Edit: I added the following logic._
On `bevy/query` remote requests, when `strict` is false:
- Unregistered components in `option` and `without` are ignored.
- Unregistered components in `has` are considered absent from the
entity.
- Unregistered components in `components` and `with` result in an empty
response since they specify hard requirements.

I made the `get_component_ids` function return a
`AnyhowResult<(Vec<(TypeId, ComponentId)>, Vec<String>)>` instead of the
previous `AnyhowResult<Vec<(TypeId, ComponentId)>>`; that is I added the
list of unregistered components.

## Testing

I tested changes using the same procedure as in the linked issue:
```sh
cargo run --example server --features="bevy_remote"
```
In another terminal:
```sh
# Not strict:
$ curl -X POST http://localhost:15702 -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "method": "bevy/query", "id": 0, "params": { "data": { "components": [ "foo::bar::MyComponent" ] } } }'
{"jsonrpc":"2.0","id":0,"result":[]}

# Strict:
$ curl -X POST http://localhost:15702 -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "method": "bevy/query", "id": 0, "params": { "data": { "components": [ "foo::bar::MyComponent" ] }, "strict": true } }'
{"jsonrpc":"2.0","id":0,"error":{"code":-23402,"message":"Component `foo::bar::MyComponent` isn't registered or used in the world"}}
```
2025-05-26 15:26:46 +00:00
ickshonpe
c8872c7665
Allow access to font atlases (#18851)
# Objective
It's not possible atm for third-party crates to create their own text
systems that use the existing cosmic-text `FontSystem` and font atlases.

## Solution
Make `FontFaceInfo`, `TextPipeline::map_handle_to_font_id`, and
`load_font_fontdb` public so third-party crates can access and update
the existing font atlases
2025-05-26 15:22:56 +00:00
ickshonpe
3edacb5e46
Enable accessibility features for the button example (#18749)
# Objective

Accessibility features don't work with the UI `button` example because
`InputFocus` must be set for the accessibility systems to recognise the
button.

Fixes #18760

## Solution

* Set the button entity as the `InputFocus` when it is hovered or
pressed.
* Call `set_changed` on the `Button` component when the button's state
changes to hovered or pressed (the accessibility system's only update
the button's state when the `Button` component is marked as changed).

## Testing

Install NVDA, it should say "hover" when the button is hovered and
"pressed" when the button is pressed.

The bounds of the accessibility node are reported incorrectly. I thought
we fixed this, I'll take another look at it. It's not a problem with
this PR.
2025-05-26 15:19:55 +00:00
Greeble
ed0266b5a6
Fix glTF importer wrongly ignoring sampler filters (#19118)
## Objective

Fix #19114.

## Solution

#17875 changed the glTF importer to make sure that sampler filters are
linear when anisotropic filtering is enabled - this is required by
`wgpu`. But the condition was mistakenly inverted, so it forces the
filtering to linear when anisotropic filtering is _not_ enabled.

## Testing

```
cargo run --example color_grading
cargo run --example testbed_3d
```
2025-05-26 13:20:03 +00:00
Ben Frankel
3d9fc5ca10
Register some types (#19361)
# Objective

Fill in some `Reflect` and `app.register_type` gaps.

I only really wanted `GlobalZIndex` but figured I'd fill in a few others
as well.
2025-05-26 02:30:07 +00:00
Zachary Harrold
3d3746e5d0
Simplify bevy_utils Features (#19090)
# Objective

Now that `bevy_platform::cfg` is merged, we can start tidying up
features. This PR starts with `bevy_utils`.

## Solution

- Removed `serde` and `critical-section` features (they were just
re-exports of `bevy_platform` anyway)
- Removed `std`, `alloc` features, relying on `bevy_platform::cfg` to
check for availability.
- Added `parallel` feature to provide access to the `Parallel` type.
- Moved the `HashMap` type aliases into `map.rs` for better
organisation.

## Testing

- CI
2025-05-24 01:46:11 +00:00
oracle58
d7cab93495
Add debug build option to build-wasm-example (#19312)
## Objective

- Add a `--debug` flag to `build-wasm-example` to support debug builds
for WebGL2/WebGPU targets.
- Fixes #18464

## Solution
- Added `--debug` flag to build Wasm examples in debug mode.
- Default remains release mode if `--debug` is not specified.
- Updated documentation to describe the new flag and usage.

## Testing
- Verified debug and release builds for WebGL2 and WebGPU respectively.
- Confirmed wasm artifacts are placed in the correct target dir for each
build profile:
  - Debug: `target/wasm32-unknown-unknown/debug/examples/`
  - Release: `target/wasm32-unknown-unknown/release/examples/`
- Confirmed wasm-bindgen output is written to:
`examples/wasm/target/debug` , `examples/wasm/target/release`
- Haven't actually tested running the example

| Backend | Profile | Artifacts written | Build success    |
|---------|---------|-------------------|------------------|
| webgl2 | debug | ✓ | ✓ |
| webgl2 | release | ✓ | ✓ |
| webpgu | debug | ✓ | ✓ |
| webpgu | release | ✓ | ✓ |

### Examples

**Debug**
```
$ cargo run -p build-wasm-example -- --api webgl2 --debug load_gltf
```
```
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1m 02s
wasm-bindgen --out-dir examples/wasm/target/debug --out-name wasm_example --target web target/wasm32-unknown-unknown/debug/examples/load_gltf.wasm
```

**Release**
```
$ cargo run -p build-wasm-example -- --api webgl2 load_gltf`
```
```
Finished `release` profile [optimized] target(s) in 1m 08s
wasm-bindgen --out-dir examples/wasm/target/release --out-name wasm_example --target web target/wasm32-unknown-unknown/release/examples/load_gltf.wasm
```

---------

Co-authored-by: Rob Parrett <robparrett@gmail.com>
2025-05-24 01:43:57 +00:00
DaoLendaye
cf3f26f10b
Add GltfMeshName component and Deref implementations (#19331)
Stores mesh names from glTF files in GltfMeshName component rather than
Name component, making both GltfMeshName and GltfMaterialName behave
like strings via Deref.

# Objective

Fixed the side effects of #19287
Fixes Examples that modify gltf materials are broken #19322

## Solution

Add GltfMeshName component and Deref implementations

Stores mesh names from glTF files in GltfMeshName component rather than
Name component, making both GltfMeshName and GltfMaterialName behave
like strings via Deref.


## Testing

cargo run --example depth_of_field
cargo run --example lightmaps
cargo run --example mixed_lighting
They are consistent with the situation before the error occurred.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Rob Parrett <robparrett@gmail.com>
2025-05-23 20:56:48 +00:00
Lucas Franca
d3012df755
Expose LogDiagnosticsState (#19323)
# Objective

Closes #19175 
Make `LogDiagnosticsState` public to be able to edit its filters

## Solution

Make `LogDiagnosticsState` public and add methods to allow editing the
duration and filter

## Testing

`cargo run -p ci`

## Showcase

Updated `log_diagnostics` example

![image](https://github.com/user-attachments/assets/25bc00f3-40e2-4b4a-b90b-137cc1f307a5)
2025-05-23 20:56:36 +00:00
NonbinaryCoder
f95287ca9b
Diagnostic reset sum ema (#19337)
# Objective

Fix incorrect average returned by `Diagnostic` after `clear_history` is
called.

## Solution

Reset sum and ema values in `Diagnostic::clear_history`.

## Testing

I have added a cargo test for `Diagnostic::clear_history` that checks
average and smoothed average. This test passes, and should not be
platform dependent.
2025-05-22 19:04:24 +00:00
theotherphil
88e73f8eda
Fix one-character typo in SystemParam docs (#19338)
# Objective

Remove errant "a" from docs.

(I'm assuming that this sort of trivial fix is easy enough to merge that
it's worth doing, but let me know if you'd prefer me to not bother.)
2025-05-22 18:54:02 +00:00
Lucas
e2d087551e
feat: derive Serialize on Childof (#19336)
# Objective

allow serialization / deserialization on the `ChildOf` entity, for
example in network usage.
my usage was for the bevy_replicon crate, to replicate `ChildOf`.

## Solution

same implementation of serde as other types in the bevy repo

---------

Co-authored-by: Hennadii Chernyshchyk <genaloner@gmail.com>
2025-05-22 18:30:14 +00:00
Kevin Reid
870ad21238
Add documentation and examples of [ScheduleLabel] usage. (#19329)
## Objective

Add documentation useful to users of `bevy_ecs` not also using `App`.

Fixes #19270.

## Solution

* Add explanation of labels to `Schedule` documentation.
* Add example of `derive(ScheduleLabel)` to `trait ScheduleLabel`.
* Add a third example to `Schedule` which demonstrates using a schedule
via label instead of owning it directly.
* Add further explanation and links to `World::add_schedule()`, and
`World::run_schedule()`.

## Testing

Reviewed generated documentation.

Please review this documentation carefully for correctness, as I have
little experience with `bevy_ecs` and I am adding this information
because it would have helped my own past confusion, but I may still be
wrong about how things should be done.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
2025-05-22 15:55:35 +00:00
stevehello166
c9e69ac65e
Rename Condition to SystemCondition` (#19328)
# Objective
Fixes #19120 

## Solution
Use the find and replace token feature in VSCode to replace all the
`Condition`s with `SystemCondition`s. Then look through all the
documentation with find and replace to replace all the `Condition`s
there.

## Testing

- Did you test these changes? If so, how?
Yes, used cargo clippy, cargo build and cargo test.
- Are there any parts that need more testing?
Nope
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
By compiling and running bevy
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
Shouldn't be, but Fedora Linux with KDE Wayland
2025-05-22 15:50:19 +00:00
atlv
031fa25ff6
fix(picking): Location is not a Component anymore. (#19306)
# Objective

- Fixes #17390

## Solution

- Remove Component derive

## Testing

- cargo clippy
2025-05-22 01:33:01 +00:00
ickshonpe
2b59ab8e2d
Fix Anchor component inconsistancies (#18393)
## Objective

Fix the misleading 2d anchor API where `Anchor` is a component and
required by `Text2d` but is stored on a field for sprites.

Fixes #18367

## Solution

Remove the `anchor` field from `Sprite` and require `Anchor` instead.

## Migration Guide

The `anchor` field has been removed from `Sprite`. Instead the `Anchor`
component is now a required component on `Sprite`.
2025-05-21 15:32:04 +00:00
ickshonpe
bf20c630a8
UI Node Gradients (#18139)
# Objective

Allowing drawing of UI nodes with a gradient instead of a flat color.

## Solution

The are three gradient structs corresponding to the three types of
gradients supported: `LinearGradient`, `ConicGradient` and
`RadialGradient`. These are then wrapped in a `Gradient` enum
discriminator which has `Linear`, `Conic` and `Radial` variants.

Each gradient type consists of the geometric properties for that
gradient and a list of color stops.
Color stops consist of a color, a position or angle and an optional
hint. If no position is specified for a stop, it's evenly spaced between
the previous and following stops. Color stop positions are absolute, if
you specify a list of stops:
```vec![vec![ColorStop::new(RED, Val::Percent(90.), ColorStop::new(Color::GREEN, Val::Percent(10.))```
the colors will be reordered and the gradient will transition from green at 10% to red at 90%. 

Colors are interpolated between the stops in SRGB space. The hint is a normalized value that can be used to shift the mid-point where the colors are mixed 50-50.  between the stop with the hint and the following stop.

For sharp stops with no interpolated transition, place two stops at the same position.

`ConicGradient`s and RadialGradient`s have a center which is set using the new `Position` type. `Position` consists of a normalized (relative to the UI node) `Vec2` anchor point and a responsive x, y offset.

To draw a UI node with a gradient you insert the components `BackgroundGradient` and `BorderGradient`, which both newtype a vector of `Gradient`s. If you set a background color, the background color is drawn first and the gradient(s) are drawn on top.

The implementation is deliberately simple and self contained. The shader draws the gradient in multiple passes which is quite inefficient for gradients with a very large number of color stops. It's simple though and there won't be any compatibility issues. We could make gradients a specialization for `UiPipeline` but I used a separate pipeline plugin for now to ensure that these changes don't break anything. 

#### Not supported in this PR
* Interpolation in other color spaces besides SRGB. 
* Images and text: This would need some breaking changes like a `UiColor` enum type with `Color` and `Gradient` variants, to enable `BorderColor`, `TextColor`, `BackgroundColor` and `ImageNode::color` to take either a `Color` or a gradient.
* Repeating gradients

## Testing

Includes three examples that can be used for testing:
```
cargo run --example linear_gradients
cargo run --example stacked_gradients
cargo run --example radial_gradients
```

Most of the code except the components API is contained within the `bevy_ui/src/render/linear_gradients` module.
There are no changes to any existing systems or plugins except for the addition of the gradients rendering systems to the render world schedule and the `Val` changes from #18164 . 

## Showcase

![gradients](https://github.com/user-attachments/assets/a09c5bb2-f9dc-4bc5-9d17-21a6338519d3)
![stacked](https://github.com/user-attachments/assets/7a1ad28e-8ae0-41d5-85b2-aa62647aef03)
![rad](https://github.com/user-attachments/assets/48609cf1-52aa-453c-afba-3b4845f3ddec)

Conic gradients can be used to draw simple pie charts like in CSS:
![PIE](https://github.com/user-attachments/assets/4594b96f-52ab-4974-911a-16d065d213bc)
2025-05-20 14:45:22 +00:00
DaoLendaye
c62ca1a0d3
Use material name for mesh entity's Name when available (#19287)
# Objective

Fixes #19286

## Solution

Use material name for mesh entity's Name when available

## Testing

Test code, modified from examples/load_gltf.rs
```rust
//! Loads and renders a glTF file as a scene.

use bevy::{gltf::GltfMaterialName, prelude::*, scene::SceneInstanceReady};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_observer(on_scene_load)
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
    ));

    commands.spawn((DirectionalLight {
        shadows_enabled: true,
        ..default()
    },));

    commands.spawn(SceneRoot(asset_server.load(
        GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"),
    )));
}

fn on_scene_load(
    trigger: Trigger<SceneInstanceReady>,
    children: Query<&Children>,
    names: Query<&Name>,
    material_names: Query<&GltfMaterialName>,
) {
    let target = trigger.target();

    for child in children.iter_descendants(target) {
        let name = if let Ok(name) = names.get(child) {
            Some(name.to_string())
        } else {
            None
        };
        let material_name = if let Ok(name) = material_names.get(child) {
            Some(name.0.clone())
        } else {
            None
        };

        info!("Entity name:{:?} | material name:{:?}", name, material_name);
    }
}
```

---

## Showcase

Run log:
<img width="859" alt="Image"
src="https://github.com/user-attachments/assets/87daddf3-31e6-41f8-9be2-4b292da9b75a"
/>

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com>
2025-05-20 14:45:04 +00:00
Lucas Franca
55edb0b476
Fix warnings and errors reported on Rust beta (#19294)
# Objective

Fixes errors and warnings on this week's Rust beta pipeline 
*
https://github.com/bevyengine/bevy/issues/18748#issuecomment-2890820218
2025-05-19 23:56:48 +00:00
Eero Lehtinen
17914943a3
Fix spot light shadow glitches (#19273)
# Objective

Spot light shadows are still broken after fixing point lights in #19265

## Solution

Fix spot lights in the same way, just using the spot light specific
visible entities component. I also changed the query to be directly in
the render world instead of being extracted to be more accurate.

## Testing

Tested with the same code but changing `PointLight` to `SpotLight`.
2025-05-19 19:42:09 +00:00
theotherphil
70e6a9010d
Add missing words in Traversal doc comment (#19298)
# Objective

Minor docs fix - add missing "is responsible".
2025-05-19 19:34:59 +00:00
theotherphil
fe16624d3c
Add boilerplate docs for PointerHits::new and HitData::new (#19259)
# Objective

Add documentation for the last two functions in bevy_picking that are
missing them.

## Solution

Add boilerplate "Constructs an X" to `PointerHits::new()` and
`HitData::new()`.

This form of no-information documentation of `new()` functions is used
in several places in the repo, and @alice-i-cecile agreed that this is a
reasonable approach - the params are already documented on the fields
within the struct definition.

---------

Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2025-05-19 19:22:07 +00:00
ickshonpe
37b16d869d
Remove YAxisOrientation from bevy_text (#19077)
# Objective

Been looking for simplifications in the text systems as part of the text
input changes.

This enum isn't very helpful I think. We can remove it and the
associated parameters and instead just negate the glyph's y-offsets in
`extract_text2d_sprite`.

## Solution

Remove the `YAxisOrientation` enum and parameters. 
Queue text sprites relative to the top-left in `extract_text2d_sprite`
and negate the glyph's y-offset.

## Testing

The `text2d` example can be used for testing:
```
cargo run --example text2d
```
2025-05-19 19:17:20 +00:00
SpecificProtagonist
e7e9973c80
Per world error handler (#18810)
# Objective

[see original
comment](https://github.com/bevyengine/bevy/pull/18801#issuecomment-2796981745)
> Alternately, could we store it on the World instead of a global? I
think we have a World nearby whenever we call default_error_handler().
That would avoid the need for atomics or locks, since we could do
ordinary reads and writes to the World.

Global error handlers don't actually need to be global – per world is
enough. This allows using different handlers for different worlds and
also removes the restrictions on changing the handler only once.

## Solution

Each `World` can now store its own error handler in a resource.

For convenience, you can also set the default error handler for an
`App`, which applies it to the worlds of all `SubApp`s. The old behavior
of only being able to set the error handler once is kept for apps.

We also don't need the `configurable_error_handler` feature anymore now.

## Testing

New/adjusted tests for failing schedule systems & observers.

---

## Showcase

```rust
App::new()
    .set_error_handler(info)
    …
```
2025-05-19 01:35:07 +00:00
atlv
45ba5b9f03
refactor(render): cleanup add_import_to_composer (#19269)
# Objective

- Reduce nesting

## Solution

- Refactor

## Testing

- `bevy run --example=3d_scene web --open`
2025-05-18 06:30:38 +00:00
Eero Lehtinen
2db103708a
Make sure prepass notices changes in alpha mode (#19170)
# Objective

Fixes #19150

## Solution

Normally the `validate_cached_entity` in 

86cc02dca2/crates/bevy_pbr/src/prepass/mod.rs (L1109-L1126)
marks unchanged entites as clean, which makes them remain in the phase.

If a material is changed to an `alpha_mode` that isn't supposed to be
added to the prepass pipeline, the specialization system just
`continue`s and doesn't indicate to the cache that the entity is not
clean anymore.

I made these invalid entities get removed from the pipeline cache so
that they are correctly not marked clean and then removed from the
phase.

## Testing

Tested with the example code from the issue.
2025-05-18 06:28:30 +00:00
Eero Lehtinen
4d1b045855
Fix point light shadow glitches (#19265)
# Objective

Fixes #18945

## Solution

Entities that are not visible in any view (camera or light), get their
render meshes removed. When they become visible somewhere again, the
meshes get recreated and assigned possibly different ids.

Point/spot light visible entities weren't cleared when the lights
themseves went out of view, which caused them to try to queue these fake
visible entities for rendering every frame. The shadow phase cache
usually flushes non visible entites, but because of this bug it never
flushed them and continued to queue meshes with outdated ids.

The simple solution is to every frame clear all visible entities for all
point/spot lights that may or may not be visible. The visible entities
get repopulated directly afterwards. I also renamed the
`global_point_lights` to `global_visible_clusterable` to make it clear
that it includes only visible things.

## Testing

- Tested with the code from the issue.
2025-05-18 06:24:37 +00:00
atlv
eed1dc428b
fix(render): transitive shader imports now work consistently on web (#19266)
# Objective

- transitive shader imports sometimes fail to load silently and return
Ok
- Fixes #19226

## Solution

- Don't return Ok, return the appropriate error code which will retry
the load later when the dependencies load

## Testing

- `bevy run --example=3d_scene web --open`


Note: this is was theoretically a problem before the hot reloading PR,
but probably extremely unlikely to occur.
2025-05-17 19:03:47 +00:00
Niklas Eicker
6397a28bbf
Pin nightly due to miri issue (#19253)
# Objective

The required miri check is currently failing due to rust-lang/miri#4323
Let's pin nightly to yesterday to not be blocked today.

## Solution

- Pinned nightly to `nightly-2025-05-16`

## Testing

- Let's see if the pipeline is green on this PR :D
2025-05-17 11:24:20 +00:00
atlv
139515278c
Use embedded_asset to load PBR shaders (#19137)
# Objective

- Get in-engine shader hot reloading working

## Solution

- Adopt #12009
- Cut back on everything possible to land an MVP: we only hot-reload PBR
in deferred shading mode. This is to minimize the diff and avoid merge
hell. The rest shall come in followups.

## Testing

- `cargo run --example pbr --features="embedded_watcher"` and edit some
pbr shader code
2025-05-16 05:47:34 +00:00
atlv
415ffa5028
clippy: expect large variants and other small fixes (#19222)
# Objective

- Fix CI

## Solution

- Expect new lint
- See #19220

## Testing

- cargo clippy
2025-05-15 22:29:59 +00:00
mgi388
01d2b8571c
Remove dead states code from bevy_ecs (#19210)
`bevy_ecs` was meant to have the `States` and `SubStates`
`proc_macro_derive`s removed when the separate `bevy_state` [was
created](https://github.com/bevyengine/bevy/issues/13216) but they were
missed.
2025-05-14 13:19:20 +00:00
atlv
673e70c72e
Fix specular cutoff on lights with radius overlapping with mesh (#19157)
# Objective

- Fixes #13318

## Solution

- Clamp a dot product to be positive to avoid choosing a `centerToRay`
which is not on the ray but behind it.

## Testing

- Repro in #13318

Main:
<img width="963" alt="{DA2A2B99-27C7-4A76-83B6-CCB70FB57CAD}"
src="https://github.com/user-attachments/assets/afae8001-48ee-4762-9522-e247bbe3577a"
/>

This PR:
<img width="963" alt="{2C4BC3E7-C6A6-4736-A916-0366FBB618DA}"
src="https://github.com/user-attachments/assets/5bea4162-0b58-4df0-bf22-09fcb27dc167"
/>

Eevee reference:

![329697008-ff28a5f3-27f3-4e98-9cee-d836a6c76aee](https://github.com/user-attachments/assets/a1b566ab-16ee-40d3-a0b6-ad179ca0fe3a)
2025-05-12 19:14:13 +00:00
Daniel Gallups
eb0f4f76ba
Fix: Provide CPU mesh processing with MaterialBindingId (#19083)
# Objective
Fixes #19027

## Solution
Query for the material binding id if using fallback CPU processing

## Testing
I've honestly no clue how to test for this, and I imagine that this
isn't entirely failsafe :( but would highly appreciate a suggestion!

To verify this works, please run the the texture.rs example using WebGL
2.

Additionally, I'm extremely naive about the nuances of pbr. This PR is
essentially to kinda *get the ball rolling* of sorts. Thanks :)

---------

Co-authored-by: Gilles Henaux <ghx_github_priv@fastmail.com>
Co-authored-by: charlotte <charlotte.c.mcelwain@gmail.com>
2025-05-12 18:11:14 +00:00
atlv
ddc9f41519
fix windows wasm embedded assets (#19139)
# Objective

- Fix #14246

## Solution

- If building for wasm windows, add a bit of code that replaces `\\`
with `/` in the `file!()` arg

## Testing

- Used MRE https://github.com/janhohenheim/asset-crash

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-05-12 10:10:32 +00:00
Jordan Dominion
86cc02dca2
Fix macro pollution in SystemParam derive (#19155)
# Objective

Fixes #19130 

## Solution

Fully quality `Result::Ok` so as to not accidentally invoke the anyhow
function of the same name

## Testing

Tested on this minimal repro with and without change.

main.rs
```rs
use anyhow::Ok;
use bevy::ecs::system::SystemParam;

#[derive(SystemParam)]
pub struct SomeParams;

fn main() {
}
```
Cargo.toml
```toml
[package]
name = "bevy-playground"
version = "0.1.0"
edition = "2024"

[dependencies]
anyhow = "1.0.98"
bevy = { path = "../bevy" }
```
2025-05-10 02:45:25 +00:00
databasedav
95470df3c8
fix .insert_related index bound (#19134)
# Objective

resolves #19092

## Solution

- remove the `.saturating_sub` from the index transformation
- add `.saturating_add` to the internal offset calculation

## Testing

- added regression test, confirming 0 index order + testing max bound
2025-05-09 17:10:54 +00:00
urben1680
732b2e0c79
Track spawn Tick of entities, offer methods, query data SpawnDetails and query filter Spawned (#19047)
# Objective

In my own project I was encountering the issue to find out which
entities were spawned after applying commands. I began maintaining a
vector of all entities with generational information before and after
applying the command and diffing it. This was awfully complicated though
and has no constant complexity but grows with the number of entities.

## Solution

Looking at `EntyMeta` it seemed obvious to me that struct can track the
tick just as it does with `MaybeLocation`, updated from the same call.
After that it became almost a given to also introduce query data
`SpawnDetails` which offers methods to get the spawn tick and location,
and query filter `Spawned` that filters entities out that were not
spawned since the last run.

## Testing

I expanded a few tests and added new ones, though maybe I forgot a group
of tests that should be extended too. I basically searched `bevy_ecs`
for mentions of `Changed` and `Added` to see where the tests and docs
are.

Benchmarks of spawn/despawn can be found
[here](https://github.com/bevyengine/bevy/pull/19047#issuecomment-2852181374).

---

## Showcase

From the added docs, systems with equal complexity since the filter is
not archetypal:
```rs
fn system1(q: Query<Entity, Spawned>) {
    for entity in &q { /* entity spawned */ }
}

fn system2(query: Query<(Entity, SpawnDetails)>) {
    for (entity, spawned) in &query {
        if spawned.is_spawned() { /* entity spawned */ }
    }
}
```

`SpawnedDetails` has a few more methods:

```rs
fn print_spawn_details(query: Query<(Entity, SpawnDetails)>) {
    for (entity, spawn_details) in &query {
        if spawn_details.is_spawned() {
            print!("new ");
        }
        println!(
            "entity {:?} spawned at {:?} by {:?}",
            entity,
            spawn_details.spawned_at(),
            spawn_details.spawned_by()
        );        
    }
}
```

## Changes

No public api was changed, I only added to it. That is why I added no
migration guide.

- query data `SpawnDetails`
- query filter `Spawned`
- method `Entities::entity_get_spawned_or_despawned_at`
- method `EntityRef::spawned_at`
- method `EntityMut::spawned_at`
- method `EntityWorldMut::spawned_at`
- method `UnsafeEntityCell::spawned_at`
- method `FilteredEntityRef::spawned_at`
- method `FilteredEntityMut::spawned_at`
- method `EntityRefExcept::spawned_at`
- method `EntityMutExcept::spawned_at`

---------

Co-authored-by: Eagster <79881080+ElliottjPierce@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-08 14:57:33 +00:00
Eagster
12aba64900
Make entity generation a new type and remove identifier (#19121)
# Objective

This is a followup to #18704 . There's lots more followup work, but this
is the minimum to unblock #18670, etc.

This direction has been given the green light by Alice
[here](https://github.com/bevyengine/bevy/pull/18704#issuecomment-2853368129).

## Solution

I could have split this over multiple PRs, but I figured skipping
straight here would be easiest for everyone and would unblock things the
quickest.

This removes the now no longer needed `identifier` module and makes
`Entity::generation` go from `NonZeroU32` to `struct
EntityGeneration(u32)`.

## Testing

CI

---------

Co-authored-by: Mark Nokalt <marknokalt@live.com>
2025-05-08 04:03:05 +00:00
Eagster
0b4858726c
Make entity::index non max (#18704)
# Objective

There are two problems this aims to solve. 

First, `Entity::index` is currently a `u32`. That means there are
`u32::MAX + 1` possible entities. Not only is that awkward, but it also
make `Entity` allocation more difficult. I discovered this while working
on remote entity reservation, but even on main, `Entities` doesn't
handle the `u32::MAX + 1` entity very well. It can not be batch reserved
because that iterator uses exclusive ranges, which has a maximum upper
bound of `u32::MAX - 1`. In other words, having `u32::MAX` as a valid
index can be thought of as a bug right now. We either need to make that
invalid (this PR), which makes Entity allocation cleaner and makes
remote reservation easier (because the length only needs to be u32
instead of u64, which, in atomics is a big deal), or we need to take
another pass at `Entities` to make it handle the `u32::MAX` index
properly.

Second, `TableRow`, `ArchetypeRow` and `EntityIndex` (a type alias for
u32) all have `u32` as the underlying type. That means using these as
the index type in a `SparseSet` uses 64 bits for the sparse list because
it stores `Option<IndexType>`. By using `NonMaxU32` here, we cut the
memory of that list in half. To my knowledge, `EntityIndex` is the only
thing that would really benefit from this niche. `TableRow` and
`ArchetypeRow` I think are not stored in an `Option` in bulk. But if
they ever are, this would help. Additionally this ensures
`TableRow::INVALID` and `ArchetypeRow::INVALID` never conflict with an
actual row, which in a nice bonus.

As a related note, if we do components as entities where `ComponentId`
becomes `Entity`, the the `SparseSet<ComponentId>` will see a similar
memory improvement too.

## Solution

Create a new type `EntityRow` that wraps `NonMaxU32`, similar to
`TableRow` and `ArchetypeRow`.
Change `Entity::index` to this type.

## Downsides

`NonMax` is implemented as a `NonZero` with a binary inversion. That
means accessing and storing the value takes one more instruction. I
don't think that's a big deal, but it's worth mentioning.

As a consequence, `to_bits` uses `transmute` to skip the inversion which
keeps it a nop. But that also means that ordering has now flipped. In
other words, higher indices are considered less than lower indices. I
don't think that's a problem, but it's also worth mentioning.

## Alternatives

We could keep the index as a u32 type and just document that `u32::MAX`
is invalid, modifying `Entities` to ensure it never gets handed out.
(But that's not enforced by the type system.) We could still take
advantage of the niche here in `ComponentSparseSet`. We'd just need some
unsafe manual conversions, which is probably fine, but opens up the
possibility for correctness problems later.

We could change `Entities` to fully support the `u32::MAX` index. (But
that makes `Entities` more complex and potentially slightly slower.)

## Testing

- CI
- A few tests were changed because they depend on different ordering and
`to_bits` values.

## Future Work

- It might be worth removing the niche on `Entity::generation` since
there is now a different niche.
- We could move `Entity::generation` into it's own type too for clarity.
- We should change `ComponentSparseSet` to take advantage of the new
niche. (This PR doesn't change that yet.)
- Consider removing or updating `Identifier`. This is only used for
`Entity`, so it might be worth combining since `Entity` is now more
unique.

---------

Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2025-05-07 18:20:30 +00:00
Chris Russell
9e2bd8ac18
Generic SystemParam impls for Option and Result (#18766)
# Objective

Provide a generic `impl SystemParam for Option<P>` that uses system
parameter validation. This immediately gives useful impls for params
like `EventReader` and `GizmosState` that are defined in terms of `Res`.
It also allows third-party system parameters to be usable with `Option`,
which was previously impossible due to orphan rules.

Note that this is a behavior change for `Option<Single>`. It currently
fails validation if there are multiple matching entities, but with this
change it will pass validation and produce `None`.

Also provide an impl for `Result<P, SystemParamValidationError>`. This
allows systems to inspect the error if necessary, either for bubbling it
up or for checking the `skipped` flag.

Fixes #12634
Fixes #14949
Related to #18516

## Solution

Add generic `SystemParam` impls for `Option` and `Result`, and remove
the impls for specific types.

Update documentation and `fallible_params` example with the new
semantics for `Option<Single>`.
2025-05-07 18:20:08 +00:00
Periwink
60ea43d01d
Add system ticks to EntityRef/Mut WorldQuery (#19115)
# Objective

- Fixes a subset of https://github.com/bevyengine/bevy/issues/13735 by
making `EntityRef`, `EntityMut` + similar WorldQueries use the system's
change ticks when being created from within a system.
In particular, this means that `entity_ref.get_ref::<T>()` will use the
correct change ticks (the ones from the system), which matches the
behaviour of querying for `Ref<T>` directly in the system parameters.

## Solution

- Implements the solution described by
https://github.com/bevyengine/bevy/issues/13735#issuecomment-2652482918
which is to add change ticks to the `UnsafeEntityCell`

## Testing

- Added a unit test that is close to what users would encounter: before
this PR the `Added`/`Changed` filters on `Ref`s created from `EntityRef`
are incorrect.
2025-05-07 18:19:35 +00:00
Zachary Harrold
63e78fe489
Deprecated Begone! 0.16 Cleanup (#19108)
# Objective

A fair few items were deprecated in 0.16. Let's delete them now that
we're in the 0.17 development cycle!

## Solution

- Deleted items marked deprecated in 0.16.

## Testing

- CI

---

## Notes

I'm making the assumption that _everything_ deprecated in 0.16 should be
removed in 0.17. That may be a false assumption in certain cases. Please
check the items to be removed to see if there are any exceptions we
should keep around for another cycle!
2025-05-07 18:17:41 +00:00
Zachary Harrold
4051465b06
Make NonSendMarker !Send (#19107)
# Objective

In #18301, `NonSendMarker` was defined in such a way that it actually
implements `Send`. This isn't strictly a soundness issue, as its goal is
to be used as a `SystemParam`, and it _does_ appropriately mark system
access as `!Send`. It just seems odd that `NonSendMarker: Send`.

## Solution

- Made `NonSendMarker` wrap `PhantomData<*mut ()>`, which forces it to
be `!Send`.

## Testing

- CI

---

## Notes

This does mean constructing a `NonSendMarker` _value_ will require using
the `SystemParam` trait, but I think that's acceptable as the marker as
a value should be rarely required if at all.
2025-05-07 00:40:35 +00:00
SpecificProtagonist
73cde28cf8
Missing punctuation (#19097) 2025-05-06 23:01:59 +00:00
Joshua Holmes
770f10bc19
Remove remaining internal use of !Send resources (#18386)
# Objective

Remaining work for and closes #17682. First half of work for that issue
was completed in [PR
17730](https://github.com/bevyengine/bevy/pull/17730). However, the rest
of the work ended up getting blocked because we needed a way of forcing
systems to run on the main thread without the use of `!Send` resources.
That was unblocked by [PR
18301](https://github.com/bevyengine/bevy/pull/18301).

This work should finish unblocking the resources-as-components effort.

# Testing

Ran several examples using my Linux machine, just to make sure things
are working as expected and no surprises pop up.

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-05-06 22:23:59 +00:00
Joona Aalto
7b1c9f192e
Adopt consistent FooSystems naming convention for system sets (#18900)
# Objective

Fixes a part of #14274.

Bevy has an incredibly inconsistent naming convention for its system
sets, both internally and across the ecosystem.

<img alt="System sets in Bevy"
src="https://github.com/user-attachments/assets/d16e2027-793f-4ba4-9cc9-e780b14a5a1b"
width="450" />

*Names of public system set types in Bevy*

Most Bevy types use a naming of `FooSystem` or just `Foo`, but there are
also a few `FooSystems` and `FooSet` types. In ecosystem crates on the
other hand, `FooSet` is perhaps the most commonly used name in general.
Conventions being so wildly inconsistent can make it harder for users to
pick names for their own types, to search for system sets on docs.rs, or
to even discern which types *are* system sets.

To reign in the inconsistency a bit and help unify the ecosystem, it
would be good to establish a common recommended naming convention for
system sets in Bevy itself, similar to how plugins are commonly suffixed
with `Plugin` (ex: `TimePlugin`). By adopting a consistent naming
convention in first-party Bevy, we can softly nudge ecosystem crates to
follow suit (for types where it makes sense to do so).

Choosing a naming convention is also relevant now, as the [`bevy_cli`
recently adopted
lints](https://github.com/TheBevyFlock/bevy_cli/pull/345) to enforce
naming for plugins and system sets, and the recommended naming used for
system sets is still a bit open.

## Which Name To Use?

Now the contentious part: what naming convention should we actually
adopt?

This was discussed on the Bevy Discord at the end of last year, starting
[here](<https://discord.com/channels/691052431525675048/692572690833473578/1310659954683936789>).
`FooSet` and `FooSystems` were the clear favorites, with `FooSet` very
narrowly winning an unofficial poll. However, it seems to me like the
consensus was broadly moving towards `FooSystems` at the end and after
the poll, with Cart
([source](https://discord.com/channels/691052431525675048/692572690833473578/1311140204974706708))
and later Alice
([source](https://discord.com/channels/691052431525675048/692572690833473578/1311092530732859533))
and also me being in favor of it.

Let's do a quick pros and cons list! Of course these are just what I
thought of, so take it with a grain of salt.

`FooSet`:

- Pro: Nice and short!
- Pro: Used by many ecosystem crates.
- Pro: The `Set` suffix comes directly from the trait name `SystemSet`.
- Pro: Pairs nicely with existing APIs like `in_set` and
`configure_sets`.
- Con: `Set` by itself doesn't actually indicate that it's related to
systems *at all*, apart from the implemented trait. A set of what?
- Con: Is `FooSet` a set of `Foo`s or a system set related to `Foo`? Ex:
`ContactSet`, `MeshSet`, `EnemySet`...

`FooSystems`:

- Pro: Very clearly indicates that the type represents a collection of
systems. The actual core concept, system(s), is in the name.
- Pro: Parallels nicely with `FooPlugins` for plugin groups.
- Pro: Low risk of conflicts with other names or misunderstandings about
what the type is.
- Pro: In most cases, reads *very* nicely and clearly. Ex:
`PhysicsSystems` and `AnimationSystems` as opposed to `PhysicsSet` and
`AnimationSet`.
- Pro: Easy to search for on docs.rs.
- Con: Usually results in longer names.
- Con: Not yet as widely used.

Really the big problem with `FooSet` is that it doesn't actually
describe what it is. It describes what *kind of thing* it is (a set of
something), but not *what it is a set of*, unless you know the type or
check its docs or implemented traits. `FooSystems` on the other hand is
much more self-descriptive in this regard, at the cost of being a bit
longer to type.

Ultimately, in some ways it comes down to preference and how you think
of system sets. Personally, I was originally in favor of `FooSet`, but
have been increasingly on the side of `FooSystems`, especially after
seeing what the new names would actually look like in Avian and now
Bevy. I prefer it because it usually reads better, is much more clearly
related to groups of systems than `FooSet`, and overall *feels* more
correct and natural to me in the long term.

For these reasons, and because Alice and Cart also seemed to share a
preference for it when it was previously being discussed, I propose that
we adopt a `FooSystems` naming convention where applicable.

## Solution

Rename Bevy's system set types to use a consistent `FooSet` naming where
applicable.

- `AccessibilitySystem` → `AccessibilitySystems`
- `GizmoRenderSystem` → `GizmoRenderSystems`
- `PickSet` → `PickingSystems`
- `RunFixedMainLoopSystem` → `RunFixedMainLoopSystems`
- `TransformSystem` → `TransformSystems`
- `RemoteSet` → `RemoteSystems`
- `RenderSet` → `RenderSystems`
- `SpriteSystem` → `SpriteSystems`
- `StateTransitionSteps` → `StateTransitionSystems`
- `RenderUiSystem` → `RenderUiSystems`
- `UiSystem` → `UiSystems`
- `Animation` → `AnimationSystems`
- `AssetEvents` → `AssetEventSystems`
- `TrackAssets` → `AssetTrackingSystems`
- `UpdateGizmoMeshes` → `GizmoMeshSystems`
- `InputSystem` → `InputSystems`
- `InputFocusSet` → `InputFocusSystems`
- `ExtractMaterialsSet` → `MaterialExtractionSystems`
- `ExtractMeshesSet` → `MeshExtractionSystems`
- `RumbleSystem` → `RumbleSystems`
- `CameraUpdateSystem` → `CameraUpdateSystems`
- `ExtractAssetsSet` → `AssetExtractionSystems`
- `Update2dText` → `Text2dUpdateSystems`
- `TimeSystem` → `TimeSystems`
- `AudioPlaySet` → `AudioPlaybackSystems`
- `SendEvents` → `EventSenderSystems`
- `EventUpdates` → `EventUpdateSystems`

A lot of the names got slightly longer, but they are also a lot more
consistent, and in my opinion the majority of them read much better. For
a few of the names I took the liberty of rewording things a bit;
definitely open to any further naming improvements.

There are still also cases where the `FooSystems` naming doesn't really
make sense, and those I left alone. This primarily includes system sets
like `Interned<dyn SystemSet>`, `EnterSchedules<S>`, `ExitSchedules<S>`,
or `TransitionSchedules<S>`, where the type has some special purpose and
semantics.

## Todo

- [x] Should I keep all the old names as deprecated type aliases? I can
do this, but to avoid wasting work I'd prefer to first reach consensus
on whether these renames are even desired.
- [x] Migration guide
- [x] Release notes
2025-05-06 15:18:03 +00:00
axlitEels
775fae5b62
Add image sampler configuration in GLTF loader (#17875)
I can't underrate anisotropic filtering.

# Objective

- Allow easily enabling anisotropic filtering on glTF assets.
- Allow selecting `ImageFilterMode` for glTF assets at runtime.

## Solution

- Added a Resource `DefaultGltfImageSampler`: it stores
`Arc<Mutex<ImageSamplerDescriptor>>` and the same `Arc` is stored in
`GltfLoader`. The default is independent from provided to `ImagePlugin`
and is set in the same way but with `GltfPlugin`. It can then be
modified at runtime with `DefaultGltfImageSampler::set`.
- Added two fields to `GltfLoaderSettings`: `default_sampler:
Option<ImageSamplerDescriptor>` to override aforementioned global
default descriptor and `override_sampler: bool` to ignore glTF sampler
data.

## Showcase

Enabling anisotropic filtering as easy as:
```rust
app.add_plugins(DefaultPlugins.set(GltfPlugin{
    default_sampler: ImageSamplerDescriptor {
        min_filter: ImageFilterMode::Linear,
        mag_filter: ImageFilterMode::Linear,
        mipmap_filter: ImageFilterMode::Linear,
        anisotropy_clamp: 16,
        ..default()
    },
    ..default()
}))
```

Use code below to ignore both the global default sampler and glTF data,
having `your_shiny_sampler` used directly for all textures instead:
```rust
commands.spawn(SceneRoot(asset_server.load_with_settings(
    GltfAssetLabel::Scene(0).from_asset("models/test-scene.gltf"),
    |settings: &mut GltfLoaderSettings| {
        settings.default_sampler = Some(your_shiny_sampler);
        settings.override_sampler = true;
    }
)));
```
Remove either setting to get different result! They don't come in pair!

Scene rendered with trillinear texture filtering:

![Trillinear](https://github.com/user-attachments/assets/be4c417f-910c-4806-9e64-fd2c21b9fd8d)
Scene rendered with 16x anisotropic texture filtering:
![Anisotropic Filtering
x16](https://github.com/user-attachments/assets/68190be8-aabd-4bef-8e97-d1b5124cce60)

## Migration Guide

- The new fields in `GltfLoaderSettings` have their default values
replicate previous behavior.

---------

Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com>
2025-05-06 05:48:13 +00:00
Christian Hughes
7e51f60de1
Add IntoSystem::with_input and ::with_input_from system wrappers (#18067)
# Objective

Originally [provided as a solution to a user's problem in
Discord](https://discord.com/channels/691052431525675048/1247654592838111302/1344431131277394042),
library authors might find the need to present user-registered systems
with system-specific data. Typically `Local<T>` is used for this type of
thing, but its not generally feasible or possible to configure/set the
underlying `T` data for locals. Alternatively, we can use `SystemInput`
to pass the data.

## Solution

- Added `IntoSystem::with_input`: Allows system-specific data to be
passed in explicitly.
- Added `IntoSystem::with_input_from`: Allows system-specific data to be
created at initialization time via `FromWorld`.

## Testing

Added two new tests, testing each of `with_input` and `with_input_from`.
2025-05-06 05:46:30 +00:00
Han Kruiger
b8724c21ce
implement MapEntities for higher-order types (#19071)
# Objective

With the current `MapEntities` `impl`s, it is not possible to derive
things like this:

```rust
#[derive(Component)]
pub struct Inventory {
  #[entities]
  slots: Vec<Option<Entity>>,
}
```

This is because `MapEntities` is only implemented for `Vec<Entity>` &
`Option<Entity>`, and not arbitrary combinations of those.

It would be nice to also support those types.

## Solution

I replaced the `impl`s of the following types

- `Option<Entity>`: replaced with `Option<T>` 
- `Vec<Entity>`: replaced with `Vec<T>`
- `HashSet<Entity, S>`: replaced with `HashSet<T, S>`
- `T` also had to be `Eq + core:#️⃣:Hash` here. **Not sure if this is
too restrictive?**
- `IndexSet<Entity, S>`: replaced with `IndexSet <T, S>`
- `T` also had to be `Eq + core:#️⃣:Hash` here. **Not sure if this is
too restrictive?**
- `BTreeSet<Entity>`: replaced with `BTreeSet<T>`
- `VecDeque<Entity>`: replaced with `VecDeque<T>`
- `SmallVec<A: smallvec::Array<Item = Entity>>`: replaced with
`SmallVec<A: smallvec::Array<Item = T>>`

(in all of the above, `T` is a generic type that implements
`MapEntities` (`Entity` being one of them).)

## Testing

I did not test any of this, but extended the `Component::map_entities`
doctest with an example usage of the newly supported types.

---

## Showcase

With these changes, this is now possible:

```rust
#[derive(Component)]
pub struct Inventory {
  #[entities]
  slots: Vec<Option<Entity>>,
}
```
2025-05-06 05:24:37 +00:00
UkoeHB
8f3e45b45f
Expose CustomCursorUrl (#19006)
# Objective

`CustomCursorUrl` is inaccessible.

## Solution

Expose `CustomCursorUrl`.
2025-05-06 05:23:48 +00:00
urben1680
2ae1510f89
Add world and world_mut methods to RelatedSpawner (#18880)
# Objective

`RelatedSpawnerCommands` offers methods to get the underlying
`Commands`.
`RelatedSpawner` does not expose the inner `World` reference so far.

I currently want to write extension traits for both of them but I need
to duplicate the whole API for the latter because I cannot get it's
`&mut World`.

## Solution

Add methods for immutable and mutable `World` access
2025-05-06 05:18:56 +00:00
Corvus
a312170749
Increase upper limit of children! (#18865)
# Objective

Currently, `bevy_ecs`'s `children!` macro only supports spawning up to
twelve children at once. Ideally there would be no limit.

## Solution

`children!` is limited because `SpawnableList`, [the primary trait bound
here](https://docs.rs/bevy/0.16.0-rc.5/bevy/ecs/hierarchy/struct.Children.html#method.spawn),
uses the fake variadics pattern on tuples of up to twelve elements.
However, since a tuple itself implements `SpawnableList`, we can simply
nest tuples of entities when we run out of room.

This PR achieves this using `macro_rules` macros with a bit of brute
force, following [some discussion on
Discord](https://discord.com/channels/691052431525675048/692572690833473578/1362174415458013314).
If we create patterns for lists of up to eleven bundles, then use a
repetition pattern to handle the rest, we can "special-case" the
recursion into a nested tuple.

In principle, this would permit an arbitrary number of children, but
Rust's recursion limits will cut things short at around 1400 elements by
default. Of course, it's generally not a good idea to stick that many
bundles in a single invocation, but it might be worth mentioning in the
docs.

## Implementation notes

### Why are cases 0-11 expanded by hand?

We could make use of a tertiary macro:

```rs
macro_rules! recursive_spawn {
    // so that this...
    ($a:expr, $b:expr) => {
        (
            $crate::spawn::Spawn($a),
            $crate::spawn::Spawn($b),
        )
    };
    
    // becomes this...
    ($a:expr, $b:expr) => {
        $crate::spawn_tuple!($a, $b)
    };
}
```

But I already feel a little bad exporting `recursive_spawn`. I'd really
like to avoid exposing more internals, even if they are annotated with
`#[doc(hidden)]`. If I had to guess, I'd say it'll also make the
expansion a tiny bit slower.

### Do we really need to handle up to twelve elements in the macro?

The macro is a little long, but doing it this way maximizes the
"flatness" of the types to be spawned. This should improve the codegen a
bit and makes the macro output a little bit easier to look at.

## Future work

The `related!` macro is essentially the same as `children!`, so if this
direction is accepted, `related!` should receive the same treatment. I
imagine we'd want to extract out the `recursive_spawn` macro into its
own file since it can be used for both. If this should be tackled in
this PR, let me know!

## Testing

This change is fairly trivial, but I added a single test to verify that
it compiles and nothing goes wrong once recursion starts happening. It's
pretty easy to verify that the change works in practice -- just spawn
over twelve entities as children at once!
2025-05-06 00:58:30 +00:00
Zachary Harrold
aadd3a3ec2
Create bevy_platform::cfg for viral feature management (#18822)
# Objective

- Acts on certain elements of #18799
- Closes #1615
- New baseline for #18170

## Solution

- Created a new `cfg` module in `bevy_platform` which contains two
macros to aid in working with features like `web`, `std`, and `alloc`.
- `switch` is a stable implementation of
[`cfg_match`](https://doc.rust-lang.org/std/macro.cfg_match.html), which
itself is a `core` alternative to [`cfg_if`](https://docs.rs/cfg-if).
- `define_alias` is a `build.rs`-free alternative to
[`cfg_aliases`](https://docs.rs/cfg_aliases) with the ability to share
feature information between crates.
- Switched to these macros within `bevy_platform` to demonstrate usage. 

## Testing

- CI

---

## Showcase

Consider the typical `std` feature as an example of a "virality". With
just `bevy_platform`, `bevy_utils`, and `bevy_ecs`, we have 3 crates in
a chain where activating `std` in any of them should really activate it
everywhere. The status-quo for this is for each crate to define its own
`std` feature, and ensure it includes the `std` feature of every
dependency in that feature. For crates which don't even interact with
`std` directly, this can be quite cumbersome. Especially considering
that Bevy has a fundamental crate, `bevy_platform`, which is a
dependency for effectively every crate.

Instead, we can use `define_alias` to create a macro which will
conditionally compile code if and only if the specified configuration
condition is met _in the defining crate_.

```rust
// In `bevy_platform`

define_alias! {
    #[cfg(feature = "std")] => {
        /// Indicates the `std` crate is available and can be used.
        std
    }
    #[cfg(all(target_arch = "wasm32", feature = "web"))] => {
        /// Indicates that this target has access to browser APIs.
        web
    }
}
```

The above `web` and `std` macros will either no-op the provided code if
the conditions are not met, or pass it unmodified if it is met. Since it
is evaluated in the context of the defining crate, `bevy_platform/std`
can be used to conditionally compile code in `bevy_utils` and `bevy_ecs`
_without_ those crates including their own `std` features.

```rust
// In `bevy_utils`
use bevy_platform::cfg;

// If `bevy_platform` has `std`, then we can too!
cfg::std! {
    extern crate std;
}
```

To aid in more complex configurations, `switch` is provided to provide a
`cfg_if` alternative that is compatible with `define_alias`:

```rust
use bevy_platform::cfg;

cfg::switch! {
    #[cfg(feature = "foo")] => { /* use the foo API */ }
    cfg::web => { /* use browser API */ }
    cfg::std => { /* use std */ }
    _ => { /* use a fallback implementation */ }
}
```

This paradigm would allow Bevy's sub-crates to avoid re-exporting viral
features, and also enable functionality in response to availability in
their dependencies, rather than from explicit features (bottom-up
instead of top-down). I imagine that a "full rollout" of this paradigm
would remove most viral features from Bevy's crates, leaving only
`bevy_platform`, `bevy_internal`, and `bevy` (since `bevy`/`_internal`
are explicitly re-exports of all of Bevy's crates).

This bottom-up approach may be useful in other areas of Bevy's features
too. For example, `bevy_core_pipeline/tonemapping_luts` requires:
- bevy_render/ktx2
- bevy_image/ktx2
- bevy_image/zstd

If `define_alias` was used in `bevy_image`, `bevy_render` would not need
to re-export the `ktx2` feature, and `bevy_core_pipeline` could directly
probe `bevy_image` for the status of `ktx2` and `zstd` features to
determine if it should compile the `tonemapping_luts` functionality,
rather than having an explicitly feature. Of course, an explicit feature
is still important for _features_, so this may not be the best example,
but it highlights that with this paradigm crates can reactively provide
functionality, rather than needing to proactively declare feature
combinations up-front and hope the user enables them.

---------

Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
2025-05-06 00:52:15 +00:00
mgi388
7a1fcb7fe7
Rename StateScoped to DespawnOnExitState and add DespawnOnEnterState (#18818)
# Objective

- Alternative to and builds on top of #16284.
- Fixes #15849.

## Solution

- Rename component `StateScoped` to `DespawnOnExitState`.
- Rename system `clear_state_scoped_entities` to
`despawn_entities_on_exit_state`.
- Add `DespawnOnEnterState` and `despawn_entities_on_enter_state` which
is the `OnEnter` equivalent.

> [!NOTE]
> Compared to #16284, the main change is that I did the rename in such a
way as to keep the terms `OnExit` and `OnEnter` together. In my own
game, I was adding `VisibleOnEnterState` and `HiddenOnExitState` and
when naming those, I kept the `OnExit` and `OnEnter` together. When I
checked #16284 it stood out to me that the naming was a bit awkward.
Putting the `State` in the middle and breaking up `OnEnter` and `OnExit`
also breaks searching for those terms.

## Open questions

1. Should we split `enable_state_scoped_entities` into two functions,
one for the `OnEnter` and one for the `OnExit`? I personally have zero
need thus far for the `OnEnter` version, so I'd be interested in not
having this enabled unless I ask for it.
2. If yes to 1., should we follow my lead in my `Visibility` state
components (see below) and name these
`app.enable_despawn_entities_on_enter_state()` and
`app.enable_despawn_entities_on_exit_state()`, which IMO says what it
does on the tin?

## Testing

Ran all changed examples.

## Side note: `VisibleOnEnterState` and `HiddenOnExitState`

For reference to anyone else and to help with the open questions, I'm
including the code I wrote for controlling entity visibility when a
state is entered/exited.

<details>
<summary>visibility.rs</summary>

```rust
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_reflect::prelude::*;
use bevy_render::prelude::*;
use bevy_state::{prelude::*, state::StateTransitionSteps};
use tracing::*;

pub trait AppExtStates {
    fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self;

    fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self;
}

impl AppExtStates for App {
    fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self {
        self.main_mut()
            .enable_visible_entities_on_enter_state::<S>();
        self
    }

    fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self {
        self.main_mut().enable_hidden_entities_on_exit_state::<S>();
        self
    }
}

impl AppExtStates for SubApp {
    fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self {
        if !self
            .world()
            .contains_resource::<Events<StateTransitionEvent<S>>>()
        {
            let name = core::any::type_name::<S>();
            warn!("Visible entities on enter state are enabled for state `{}`, but the state isn't installed in the app!", name);
        }
        // We work with [`StateTransition`] in set
        // [`StateTransitionSteps::ExitSchedules`] as opposed to [`OnExit`],
        // because [`OnExit`] only runs for one specific variant of the state.
        self.add_systems(
            StateTransition,
            update_to_visible_on_enter_state::<S>.in_set(StateTransitionSteps::ExitSchedules),
        )
    }

    fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self {
        if !self
            .world()
            .contains_resource::<Events<StateTransitionEvent<S>>>()
        {
            let name = core::any::type_name::<S>();
            warn!("Hidden entities on exit state are enabled for state `{}`, but the state isn't installed in the app!", name);
        }
        // We work with [`StateTransition`] in set
        // [`StateTransitionSteps::ExitSchedules`] as opposed to [`OnExit`],
        // because [`OnExit`] only runs for one specific variant of the state.
        self.add_systems(
            StateTransition,
            update_to_hidden_on_exit_state::<S>.in_set(StateTransitionSteps::ExitSchedules),
        )
    }
}

#[derive(Clone, Component, Debug, Reflect)]
#[reflect(Component, Debug)]
pub struct VisibleOnEnterState<S: States>(pub S);

#[derive(Clone, Component, Debug, Reflect)]
#[reflect(Component, Debug)]
pub struct HiddenOnExitState<S: States>(pub S);

/// Makes entities marked with [`VisibleOnEnterState<S>`] visible when the state
/// `S` is entered.
pub fn update_to_visible_on_enter_state<S: States>(
    mut transitions: EventReader<StateTransitionEvent<S>>,
    mut query: Query<(&VisibleOnEnterState<S>, &mut Visibility)>,
) {
    // We use the latest event, because state machine internals generate at most
    // 1 transition event (per type) each frame. No event means no change
    // happened and we skip iterating all entities.
    let Some(transition) = transitions.read().last() else {
        return;
    };
    if transition.entered == transition.exited {
        return;
    }
    let Some(entered) = &transition.entered else {
        return;
    };
    for (binding, mut visibility) in query.iter_mut() {
        if binding.0 == *entered {
            visibility.set_if_neq(Visibility::Visible);
        }
    }
}

/// Makes entities marked with [`HiddenOnExitState<S>`] invisible when the state
/// `S` is exited.
pub fn update_to_hidden_on_exit_state<S: States>(
    mut transitions: EventReader<StateTransitionEvent<S>>,
    mut query: Query<(&HiddenOnExitState<S>, &mut Visibility)>,
) {
    // We use the latest event, because state machine internals generate at most
    // 1 transition event (per type) each frame. No event means no change
    // happened and we skip iterating all entities.
    let Some(transition) = transitions.read().last() else {
        return;
    };
    if transition.entered == transition.exited {
        return;
    }
    let Some(exited) = &transition.exited else {
        return;
    };
    for (binding, mut visibility) in query.iter_mut() {
        if binding.0 == *exited {
            visibility.set_if_neq(Visibility::Hidden);
        }
    }
}
```

</details>

---------

Co-authored-by: Benjamin Brienen <Benjamin.Brienen@outlook.com>
Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
2025-05-06 00:37:04 +00:00
Tim Overbeek
60cdefd128
Derive clone_behavior for Components (#18811)
Allow Derive(Component) to specify a clone_behavior

```rust
#[derive(Component)]
#[component(clone_behavior = Ignore)]
MyComponent;
```
2025-05-06 00:32:59 +00:00
MichiRecRoom
b19f644c2f
The toml workflow job will now install taplo-cli using cargo-binstall (#18773)
# Objective
Avoid needing to compile `taplo-cli` every time we use it in CI.

## Solution
Use [cargo-binstall](https://github.com/cargo-bins/cargo-binstall) to
install `taplo-cli`.

cargo-binstall is different from `cargo install`, in that it will first
attempt download a precompiled `taplo-cli` binary, in an attempt to
avoid compilation. However, failing that (for any reason), it will fall
back to installing the binary through `cargo install`.

While installing `taplo-cli` from source is relatively fast (around
50-60s), this still provides a small speed boost to the job, by not
needing to spend time compiling `taplo-cli` from source at all.

## Note on how this affects workflows
This PR does have one side-effect: Should `taplo-cli` need to be
compiled from source at all, it is no longer guaranteed to use the
latest `stable` version of `rustc`. This may be considered problematic,
as `taplo-cli` doesn't appear to have a MSRV policy.

However, its MSRV (as of writing this PR) is `1.74` - a nearly 1.5 year
old version. This seems to imply that, if `taplo-cli`'s MSRV is ever
updated, it won't be to the absolute latest stable version of Rust until
said version is a few months old.

Combine that with [the Github Actions runner images being frequently
(and automatically) updated to use the latest Rust
tooling](https://github.com/actions/runner-images/pull/11957), and I
don't foresee `taplo-cli`'s MSRV being an issue in 99% of circumstances.

Still, there is the possibility of it being a problem in those 1% of
circumstances - if this is a concern, please let me know and I'll try to
fix it.

## Testing

This change was tested on my local fork. The specific job run can be
found
[here](https://github.com/LikeLakers2/bevy/actions/runs/14350945588/job/40229485624).

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-05-06 00:26:10 +00:00
Eagster
af8d12c3e1
deprecate SimpleExecutor (#18753)
# Objective

Contributes to #18741 and #18453.

## Solution

Deprecate `SimpleExecutor`. If users run into migration issues, we can
backtrack. Otherwise, we follow this up with #18741

We can't easily deprecate the module too because of
[this](https://github.com/rust-lang/rust/issues/47238).

## Testing

CI

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Cyrill Schenkel <cyrill.schenkel@gmail.com>
2025-05-06 00:21:57 +00:00
Jonathan Chan Kwan Yin
cdcb773e9b
Add EntityWorldMut::reborrow_scope() (#18730)
# Objective

Allow `EntityCommand` implementors to delegate to other entity commands
easily:

```rs
impl EntityCommand for Foo {
    fn apply(self, mut entity: EntityWorldMut) {
        entity.reborrow_scope(|e| StepOne.apply(e));
        entity.reborrow_scope(|e| StepTwo.apply(e));
    }
}
```
2025-05-06 00:19:56 +00:00
re0312
5ed8e0639a
Merge ObserverState and Observer into single component (#18728)
# Objective

- bevy removed `Observe` type parameters in #15151 ,it enables merging
`Observer` and `ObserverState ` into a single component. with this
consolidation ,we can improve efficiency while reducing boilerplate.

## Solution

- remove `ObserverState `and merge it  into `Observer`

## Testing

40%~60% performance win due to removal of redundant look up.

![image](https://github.com/user-attachments/assets/eb1d46cb-cca3-4c2b-948c-bf4ecb617de9)

This also improves ergonomics when using dynamic observer
```rust
// previously 
world.spawn(ObserverState {
            // SAFETY: we registered `event_a` above and it matches the type of EventA
            descriptor: unsafe { ObserverDescriptor::default().with_events(vec![event_a]) },
            runner: |mut world, _trigger, _ptr, _propagate| {
                world.resource_mut::<Order>().observed("event_a");
            },
            ..Default::default()
        });

// now
let observe = unsafe {
    Observer::with_dynamic_runner(|mut world, _trigger, _ptr, _propagate| {
        world.resource_mut::<Order>().observed("event_a");
    })
    .with_event(event_a)
};
world.spawn(observe);
```
2025-05-06 00:12:27 +00:00
Chris Russell
3442e2556d
Use new run_without_applying_deferred method in SingleThreadedExecutor (#18684)
# Objective

Simplify code in the `SingleThreadedExecutor` by removing a special case
for exclusive systems.

The `SingleThreadedExecutor` runs systems without immediately applying
deferred buffers. That required calling `run_unsafe()` instead of
`run()`, but that would `panic` for exclusive systems, so the code also
needed a special case for those. Following #18076 and #18406, we have a
`run_without_applying_deferred` method that has the exact behavior we
want and works on exclusive systems.

## Solution

Replace the code in `SingleThreadedExecutor` that runs systems with a
single call to `run_without_applying_deferred()`. Also add this as a
wrapper in the `__rust_begin_short_backtrace` module to preserve the
special behavior for backtraces.
2025-05-06 00:09:02 +00:00
Greeble
49f1827633
Speed up ECS benchmarks by limiting variations (#18659)
## Objective

Reduce the time spent on ECS benchmarks without significantly
compromising coverage.

## Background

A `cargo bench -p benches --bench ecs` takes about 45 minutes. I'm
guessing this bench is mainly used to check for regressions after ECS
changes, and requiring 2x45 minute tests means that most people will
skip benchmarking entirely.

I noticed that some benches are repeated with sizes from long linear
progressions (10, 20, ..., 100). This might be nice for detailed
profiling, but seems too much for a overall regression check.

## Solution

The PR follows the principles of "three or four different sizes is fine"
and "powers of ten where it fits". The number of benches is reduced from
394 to 238 (-40%), and time from 46.2 minutes to 32.8 (-30%).

While some coverage is lost, I think it's reasonable for anyone doing
detailed profiling of a particular feature to temporarily add more
benches.

There's a couple of changes to avoid leading zeroes. I felt that `0010,
0100, 1000` is harder to read than `10, 100, 1000`.

## Is That Enough?

32 minutes is still too much. Possible future options:

- Reduce measurement and warmup times. I suspect the current times
(mostly 4-5 seconds total) are too conservative, and 1 second would be
fine for spotting significant regressions.
- Split the bench into quick and detailed variants.

## Testing

```
cargo bench -p benches --bench ecs
```
2025-05-06 00:07:18 +00:00
Mincong Lu
023b502153
Implemented Alpha for f32. (#18653)
# Objective

`f32` can be used to represent alpha, this streamlines generic code
related to colors.

## Solution

- Implemented `Alpha` for `f32`.
2025-05-06 00:00:17 +00:00
Mincong Lu
818459113e
Added StableInterpolate implementations for linear colors. (#18601)
# Objective

Colors currently do not implement `StableInterpolate`, which makes them
ineligible for functions like `smooth_nudge` and make some generic APIs
awkward.

## Solution

Implemented `StableInterpolate` for linear color types that should be
uncontroversial. Non-linear types like `Hsl` are not implemented in this
PR.

## Testing

Added a test that checks implementations are correct.
2025-05-05 23:58:56 +00:00
andriyDev
798e1c5498
Move initializing the ScreenshotToScreenPipeline to the ScreenshotPlugin. (#18524)
# Objective

- Minor cleanup.
- This seems to have been introduced in #8336. There is no discussion
about it I can see, there's no comment explaining why this is here and
not in `ScreenshotPlugin`. This seems to have just been misplaced.

## Solution

- Move this to the ScreenshotPlugin!

## Testing

- The screenshot example still works at least on desktop.
2025-05-05 23:56:22 +00:00
Eagster
f6543502b4
Add BundleRemover (#18521)
# Objective

It has long been a todo item in the ecs to create a `BundleRemover`
alongside the inserter, spawner, etc.

This is an uncontroversial first step of #18514.

## Solution

Move existing code from complex helper functions to one generalized
`BundleRemover`.

## Testing

Existing tests.
2025-05-05 23:55:04 +00:00
Chris Russell
bea0a0a9bc
Let FilteredEntity(Ref|Mut) receive access when nested. (#18236)
# Objective

Let `FilteredEntityRef` and `FilteredEntityMut` receive access when
nested inside tuples or `#[derive(QueryData)]` types. Make sure to
exclude any access that would conflict with other subqueries!

Fixes #14349

## Solution

Replace `WorldQuery::set_access(state, access)` with a new method,
`QueryData::provide_extra_access(state, access, available_access)`, that
passes both the total available access and the currently used access.
This is called after `WorldQuery::update_component_access()`, so any
access used by ordinary subqueries will be known. `FilteredEntityRef`
and `FilteredEntityMut` can use the combination to determine how much
access they can safely take, while tuples can safely pass those
parameters directly to their subqueries.

This requires a new `Access::remove_conflicting_access()` method that
can be used to remove any access that would conflict with existing
access. Implementing this method was easier by first factoring some
common set manipulation code out of `Access::extend`. I can extract that
refactoring to a separate PR if desired.

Have `FilteredEntity(Ref|Mut)` store `Access` instead of
`FilteredAccess` because they do not need to keep track of the filter.
This was necessary in an early draft but no longer is. I left it in
because it's small and I'm touching that code anyway, but I can extract
it to a separate PR if desired.
2025-05-05 23:23:46 +00:00
NiseVoid
02d569d0e4
Add Allows filter to bypass DefaultQueryFilters (#18192)
# Objective

Fixes #17803 

## Solution

- Add an `Allows<T>` `QueryFilter` that adds archetypal access for `T`
- Fix access merging to include archetypal from both sides

## Testing

- Added a case to the unit test for the application of
`DefaultQueryFilters`
2025-05-05 23:21:26 +00:00
Eagster
bfc76c589e
Remove insert_or_spawn function family (#18148)
# Objective

Based on and closes #18054, this PR builds on #18035 and #18147 to
remove:

- `Commands::insert_or_spawn_batch`
- `Entities::alloc_at_without_replacement`
- `Entities::alloc_at`
- `entity::AllocAtWithoutReplacement`
- `World::insert_or_spawn_batch`
- `World::insert_or_spawn_batch_with_caller`

## Testing

Just removing unused, deprecated code, so no new tests. Note that as of
writing, #18035 is still under testing and review.

## Future Work

Per
[this](https://github.com/bevyengine/bevy/issues/18054#issuecomment-2689088899)
comment on #18054, there may be additional performance improvements
possible to the entity allocator now that `alloc_at` no longer is
supported. At a glance, I don't see anything obvious to improve, but it
may be worth further investigation in the future.

---------

Co-authored-by: JaySpruce <jsprucebruce@gmail.com>
2025-05-05 23:14:32 +00:00
Jean Mertz
3b24f520b9
feat(log): support customizing default log formatting (#17722)
The LogPlugin now allows overriding the default
`tracing_subscriber::fmt::Layer` through a new `fmt_layer` option. This
enables customization of the default log output format without having to
replace the entire logging system.

For example, to disable timestamps in the log output:

```rust
fn fmt_layer(_app: &mut App) -> Option<bevy::log::BoxedFmtLayer> {
    Some(Box::new(
        bevy::log::tracing_subscriber::fmt::Layer::default()
            .without_time()
            .with_writer(std::io::stderr),
    ))
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(bevy::log::LogPlugin {
            fmt_layer,
            ..default()
        }))
        .run();
}
```

This is different from the existing `custom_layer` option, because that
option _adds_ additional layers to the subscriber, but can't modify the
default formatter layer (at least, not to my knowledge).

I almost always disable timestamps in my Bevy logs, and usually also
tweak other default log formatting (such as `with_span_events`), which
made it so that I always had to disable the default logger. This allows
me to use everything the Bevy logger supports (including tracy support),
while still formatting the default logs the way I like them.

---------

Signed-off-by: Jean Mertz <git@jeanmertz.com>
2025-05-05 23:01:06 +00:00
Antony
bf42cb3532
Add a viewport UI widget (#17253)
# Objective

Add a viewport widget.

## Solution

- Add a new `ViewportNode` component to turn a UI node into a viewport.
- Add `viewport_picking` to pass pointer inputs from other pointers to
the viewport's pointer.
- Notably, this is somewhat functionally different from the viewport
widget in [the editor
prototype](https://github.com/bevyengine/bevy_editor_prototypes/pull/110/files#L124),
which just moves the pointer's location onto the render target. Viewport
widgets have their own pointers.
  - Care is taken to handle dragging in and out of viewports.
- Add `update_viewport_render_target_size` to update the viewport node's
render target's size if the node size changes.
- Feature gate picking-related viewport items behind
`bevy_ui_picking_backend`.

## Testing

I've been using an example I made to test the widget (and added it as
`viewport_node`):

<details><summary>Code</summary>

```rust
//! A simple scene to demonstrate spawning a viewport widget. The example will demonstrate how to
//! pick entities visible in the widget's view.

use bevy::picking::pointer::PointerInteraction;
use bevy::prelude::*;

use bevy::ui::widget::ViewportNode;
use bevy::{
    image::{TextureFormatPixelInfo, Volume},
    window::PrimaryWindow,
};
use bevy_render::{
    camera::RenderTarget,
    render_resource::{
        Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
    },
};

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, MeshPickingPlugin))
        .add_systems(Startup, test)
        .add_systems(Update, draw_mesh_intersections)
        .run();
}

#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
struct Shape;

fn test(
    mut commands: Commands,
    window: Query<&Window, With<PrimaryWindow>>,
    mut images: ResMut<Assets<Image>>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    // Spawn a UI camera
    commands.spawn(Camera3d::default());

    // Set up an texture for the 3D camera to render to
    let window = window.get_single().unwrap();
    let window_size = window.physical_size();
    let size = Extent3d {
        width: window_size.x,
        height: window_size.y,
        ..default()
    };
    let format = TextureFormat::Bgra8UnormSrgb;
    let image = Image {
        data: Some(vec![0; size.volume() * format.pixel_size()]),
        texture_descriptor: TextureDescriptor {
            label: None,
            size,
            dimension: TextureDimension::D2,
            format,
            mip_level_count: 1,
            sample_count: 1,
            usage: TextureUsages::TEXTURE_BINDING
                | TextureUsages::COPY_DST
                | TextureUsages::RENDER_ATTACHMENT,
            view_formats: &[],
        },
        ..default()
    };
    let image_handle = images.add(image);

    // Spawn the 3D camera
    let camera = commands
        .spawn((
            Camera3d::default(),
            Camera {
                // Render this camera before our UI camera
                order: -1,
                target: RenderTarget::Image(image_handle.clone().into()),
                ..default()
            },
        ))
        .id();

    // Spawn something for the 3D camera to look at
    commands
        .spawn((
            Mesh3d(meshes.add(Cuboid::new(5.0, 5.0, 5.0))),
            MeshMaterial3d(materials.add(Color::WHITE)),
            Transform::from_xyz(0.0, 0.0, -10.0),
            Shape,
        ))
        // We can observe pointer events on our objects as normal, the
        // `bevy::ui::widgets::viewport_picking` system will take care of ensuring our viewport
        // clicks pass through
        .observe(on_drag_cuboid);

    // Spawn our viewport widget
    commands
        .spawn((
            Node {
                position_type: PositionType::Absolute,
                top: Val::Px(50.0),
                left: Val::Px(50.0),
                width: Val::Px(200.0),
                height: Val::Px(200.0),
                border: UiRect::all(Val::Px(5.0)),
                ..default()
            },
            BorderColor(Color::WHITE),
            ViewportNode::new(camera),
        ))
        .observe(on_drag_viewport);
}

fn on_drag_viewport(drag: Trigger<Pointer<Drag>>, mut node_query: Query<&mut Node>) {
    if matches!(drag.button, PointerButton::Secondary) {
        let mut node = node_query.get_mut(drag.target()).unwrap();

        if let (Val::Px(top), Val::Px(left)) = (node.top, node.left) {
            node.left = Val::Px(left + drag.delta.x);
            node.top = Val::Px(top + drag.delta.y);
        };
    }
}

fn on_drag_cuboid(drag: Trigger<Pointer<Drag>>, mut transform_query: Query<&mut Transform>) {
    if matches!(drag.button, PointerButton::Primary) {
        let mut transform = transform_query.get_mut(drag.target()).unwrap();
        transform.rotate_y(drag.delta.x * 0.02);
        transform.rotate_x(drag.delta.y * 0.02);
    }
}

fn draw_mesh_intersections(
    pointers: Query<&PointerInteraction>,
    untargetable: Query<Entity, Without<Shape>>,
    mut gizmos: Gizmos,
) {
    for (point, normal) in pointers
        .iter()
        .flat_map(|interaction| interaction.iter())
        .filter_map(|(entity, hit)| {
            if !untargetable.contains(*entity) {
                hit.position.zip(hit.normal)
            } else {
                None
            }
        })
    {
        gizmos.arrow(point, point + normal.normalize() * 0.5, Color::WHITE);
    }
}
```

</details>

## Showcase


https://github.com/user-attachments/assets/39f44eac-2c2a-4fd9-a606-04171f806dc1

## Open Questions

- <del>Not sure whether the entire widget should be feature gated behind
`bevy_ui_picking_backend` or not? I chose a partial approach since maybe
someone will want to use the widget without any picking being
involved.</del>
- <del>Is `PickSet::Last` the expected set for `viewport_picking`?
Perhaps `PickSet::Input` is more suited.</del>
- <del>Can `dragged_last_frame` be removed in favor of a better dragging
check? Another option that comes to mind is reading `Drag` and `DragEnd`
events, but this seems messier.</del>

---------

Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-05 22:57:37 +00:00
Chris Russell
55bb59b844
Stop using ArchetypeComponentId in the executor (#16885)
# Objective

Stop using `ArchetypeComponentId` in the executor. These IDs will grow
even more quickly with relations, and the size may start to degrade
performance.

## Solution

Have systems expose their `FilteredAccessSet<ComponentId>`, and have the
executor use that to determine which systems conflict. This can be
determined statically, so determine all conflicts during initialization
and only perform bit tests when running.

## Testing

I ran many_foxes and didn't see any performance changes. It's probably
worth testing this with a wider range of realistic schedules to see
whether the reduced concurrency has a cost in practice, but I don't know
what sort of test cases to use.

## Migration Guide

The schedule will now prevent systems from running in parallel if there
*could* be an archetype that they conflict on, even if there aren't
actually any. For example, these systems will now conflict even if no
entity has both `Player` and `Enemy` components:
```rust
fn player_system(query: Query<(&mut Transform, &Player)>) {}
fn enemy_system(query: Query<(&mut Transform, &Enemy)>) {}
```

To allow them to run in parallel, use `Without` filters, just as you
would to allow both queries in a single system:
```rust
// Either one of these changes alone would be enough
fn player_system(query: Query<(&mut Transform, &Player), Without<Enemy>>) {}
fn enemy_system(query: Query<(&mut Transform, &Enemy), Without<Player>>) {}
```
2025-05-05 22:52:44 +00:00
François Mockers
31c2dc591d
ignore files starting with . when loading folders (#11214)
# Objective

- When loading a folder with dot files inside, Bevy crashes:
```
thread 'IO Task Pool (1)' panicked at crates/bevy_asset/src/io/mod.rs:260:10:
asset paths must have extensions
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
- those files are common for other tools to store their
settings/metadata

## Solution

- Ignore files starting with a dot when loading folders
2025-05-05 22:42:01 +00:00
Greeble
235257ff62
Fix occlusion culling not respecting device limits (#18974)
The occlusion culling plugin checks for a GPU feature by looking at
`RenderAdapter`. This is wrong - it should be checking `RenderDevice`.
See these notes for background:
https://github.com/bevyengine/bevy/discussions/18973

I don't have any evidence that this was causing any bugs, so right now
it's just a precaution.

## Testing

```
cargo run --example occlusion_culling
```

Tested on Win10/Nvidia across Vulkan, WebGL/Chrome, WebGPU/Chrome.
2025-05-05 18:04:43 +00:00
Chris Russell
5f936aefc8
Prevent exclusive systems from being used as observers (#19033)
# Objective

Prevent using exclusive systems as observers. Allowing them is unsound,
because observers are only expected to have `DeferredWorld` access, and
the observer infrastructure will keep pointers that are invalidated by
the creation of `&mut World`.

See
https://github.com/bevyengine/bevy/actions/runs/14778342801/job/41491517847?pr=19011
for a MIRI failure in a recent PR caused by an exclusive system being
used as an observer in a test.

## Solution

Have `Observer::new` panic if `System::is_exclusive()` is true. Document
that method, and methods that call it, as panicking.

(It should be possible to express this in the type system so that the
calls won't even compile, but I did not want to attempt that.)

## Testing

Added a unit test that calls `World::add_observer` with an exclusive
system.
2025-05-05 17:46:25 +00:00
akimakinai
0f6d532a15
Sprite picking docs fix (#19016)
# Objective

- Docs in sprite picking plugin / example contain outdated information.

References:
- Sprite picking now always require `Picking` - #17842
- Transparency pass-through added - #16388

## Solution

- Fix the docs.
2025-05-05 17:45:14 +00:00
JaySpruce
113d1b7dc1
Fix sparse set components ignoring insert_if_new/InsertMode (#19059)
# Objective

I've been tinkering with ECS insertion/removal lately, and noticed that
sparse sets just... don't interact with `InsertMode` at all. Sure
enough, using `insert_if_new` with a sparse component does the same
thing as `insert`.

# Solution

- Add a check in `BundleInfo::write_components` to drop the new value if
the entity already has the component and `InsertMode` is `Keep`.
- Add necessary methods to sparse set internals to fetch the drop
function.

# Testing

Minimal reproduction:
<details>
<summary>Code</summary>

```
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(PostStartup, component_print)
        .run();
}

#[derive(Component)]
#[component(storage = "SparseSet")]
struct SparseComponent(u32);

fn setup(mut commands: Commands) {
    let mut entity = commands.spawn_empty();
    entity.insert(SparseComponent(1));
    entity.insert(SparseComponent(2));

    let mut entity = commands.spawn_empty();
    entity.insert(SparseComponent(3));
    entity.insert_if_new(SparseComponent(4));
}

fn component_print(query: Query<&SparseComponent>) {
    for component in &query {
        info!("{}", component.0);
    }
}
```

</details>

Here it is on Bevy Playground (0.15.3): 

https://learnbevy.com/playground?share=2a96a68a81e804d3fdd644a833c1d51f7fa8dd33fc6192fbfd077b082a6b1a41

Output on `main`:
```
2025-05-04T17:50:50.401328Z  INFO system{name="fork::component_print"}: fork: 2
2025-05-04T17:50:50.401583Z  INFO system{name="fork::component_print"}: fork: 4
```

Output with this PR :
```
2025-05-04T17:51:33.461835Z  INFO system{name="fork::component_print"}: fork: 2
2025-05-04T17:51:33.462091Z  INFO system{name="fork::component_print"}: fork: 3
```
2025-05-05 17:42:36 +00:00
Marius Cobzarenco
20b2b5e6b1
Fix tonemapping example when using a local image (#19061)
# Objective

- The tonemapping example allows using a local image to try out
different color grading. However, using a local file stopped working
when we added the `UnapprovedPathMode` setting to the assets plugin.

## Solution

- Set `unapproved_path_mode: UnapprovedPathMode::Allow` in the example

## Testing

- I tried out the example with local images, previously it would fail
saying it's an untrusted path.
2025-05-05 17:39:32 +00:00
Lucas Franca
54856d088d
Upgrade atomicow version (#19075)
# Objective
`atomicow` `1.0` does not have `std` feature requested by `bevy_asset`,
but `1.1` does

## Solution

Bump version
2025-05-05 17:38:31 +00:00
Greeble
b516e78317
Bump crate-ci/typos from 1.31.1 to 1.32.0 (#19072)
Adopted #19066. Bumps
[crate-ci/typos](https://github.com/crate-ci/typos) from 1.31.1 to
1.32.0.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 17:27:36 +00:00
Rob Parrett
831fe305e4
Update ktx2 to 0.4.0 (#19073)
# Objective

Adopted #19065
Closes #19065

Updates the requirements on [ktx2](https://github.com/BVE-Reborn/ktx2)
to permit the latest version.
- [Release notes](https://github.com/BVE-Reborn/ktx2/releases)
-
[Changelog](https://github.com/BVE-Reborn/ktx2/blob/trunk/CHANGELOG.md)
- [Commits](https://github.com/BVE-Reborn/ktx2/compare/v0.3.0...v0.4.0)

# Overview

- Some renames
- A `u8` became `NonZero<u8>`
- Some methods return a new `Level` struct with a `data` member instead
of raw level data.

# Testing

- Passed CI locally
- Ran several examples which utilize `ktx2` files: `scrolling_fog`,
`mixed_lighting`, `skybox`, `lightmaps`.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 16:42:36 +00:00
Zachary Harrold
ea7e868f5c
Strip unused features from bevy_asset dependencies (#18979)
# Objective

- Contributes to #18978

## Solution

- Disable default features on all dependencies in `bevy_asset` and
explicitly enable ones that are required.
- Remove `compile_error` caused by enabling `file_watcher` without
`multi_threaded` by including `multi_threaded` in `file_watcher`.

## Testing

- CI

---

## Notes

No breaking changes here, just a little cleaning before the more
controversial changes for `no_std` support.

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-05 05:51:01 +00:00
Daniel Skates
65f7b05841
Ignore RUSTSEC-2023-0089 until postcard is updated (#19038)
# Objective

- CI fails due to `atomic-polyfill` being unmaintained

## Solution

- Dependency chain of `postcard -> heapless -> atomic-polyfill` .
`heapless` is updated. `postcard` has not yet.
- See https://github.com/jamesmunns/postcard/issues/223
- Ignore the advisory for now

## Testing

- CI with this PR

---------

Co-authored-by: MichiRecRoom <1008889+LikeLakers2@users.noreply.github.com>
2025-05-05 05:50:03 +00:00
Guillaume Gomez
c286e4f5f3
Update sysinfo version to 0.35.0 (#19028)
This release is mostly about bugfixes and API/code improvements. Pretty
straightforward update. :)
2025-05-05 05:49:23 +00:00
Innokentiy Popov
2c3d20d748
Fix rotate_by implementation for Aabb2d (#19015)
# Objective

Fixes #18969 

## Solution

Also updated `Aabb3d` implementation for consistency.

## Testing

Added tests for `Aabb2d` and `Aabb3d` to verify correct rotation
behavior for angles greater than 90 degrees.
2025-05-04 13:05:27 +00:00
Brezak
e05e74a76a
Implement RelationshipSourceCollection for IndexSet (#18471)
# Objective

`IndexSet` doesn't implement `RelationshipSourceCollection`

## Solution

Implement `MapEntities` for `IndexSet`
Implement `RelationshipSourceCollection` for `IndexSet`

## Testing

`cargo clippy`

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-05-04 10:17:29 +00:00
Brezak
c6d41a0d34
Implement RelationshipSourceCollection for BTreeSet (#18469)
# Objective

`BTreeSet` doesn't implement `RelationshipSourceCollection`.

## Solution

Implement it.

## Testing

`cargo clippy`

---

## Showcase

You can now use `BTreeSet` in a `RelationshipTarget`

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-04 09:18:07 +00:00
Chris Russell
d28e4908ca
Create a When system param wrapper for skipping systems that fail validation (#18765)
# Objective

Create a `When` system param wrapper for skipping systems that fail
validation.

Currently, the `Single` and `Populated` parameters cause systems to skip
when they fail validation, while the `Res` family causes systems to
error. Generalize this so that any fallible parameter can be used either
to skip a system or to raise an error. A parameter used directly will
always raise an error, and a parameter wrapped in `When<P>` will always
cause the system to be silently skipped.

~~Note that this changes the behavior for `Single` and `Populated`. The
current behavior will be available using `When<Single>` and
`When<Populated>`.~~

Fixes #18516

## Solution

Create a `When` system param wrapper that wraps an inner parameter and
converts all validation errors to `skipped`.

~~Change the behavior of `Single` and `Populated` to fail by default.~~

~~Replace in-engine use of `Single` with `When<Single>`. I updated the
`fallible_systems` example, but not all of the others. The other
examples I looked at appeared to always have one matching entity, and it
seemed more clear to use the simpler type in those cases.~~

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-04 08:41:42 +00:00
Chris Biscardi
56405890f2
refactor ui/borders example to use new children! macro (#18962)
# Objective

Refactor
[`examples/ui/borders.rs`](7f0490655c/examples/ui/borders.rs)
to use the new spawning/hierarchy APIs in 0.16.

## Solution

This refactor reduces the number of `.spawn` calls from about 16 to 2,
using one spawn for each major feature:

* camera2d
* ui layout

The `Children::spawn` relationship API is used to take advantage of
`SpawnIter` for the borders examples in each block.

Each block of examples now returns a Bundle into its respective
variable, which is then used in combination with the new `label` widget
which makes use of the new `impl Bundle` return capability. This allows
the ui layout to use a single `.spawn` with the `children!` macro.

The blocks of examples are still in separate variables because it felt
like a useful way to organize it still, even without needing to spawn at
those locations.

Functionality of the demo hasn't changed, this is just an API/code
update.

## Showcase

![screenshot-2025-04-27-at-18 05
19@2x](https://github.com/user-attachments/assets/5f10b28b-b0f1-4a55-af9f-2e7b05b7b4bf)

<details>
<summary>Before screenshot</summary>

![screenshot-2025-04-27-at-18 17
12@2x](https://github.com/user-attachments/assets/ae159d96-ba4d-4429-b934-7779470c480a)

</details>

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-04 08:35:03 +00:00
tmstorey
c55c69e3fc
Add NonNilUuid support to bevy_reflect (#18604)
# Objective

- If using a `NonNilUuid` in Bevy, it's difficult to reflect it.

## Solution

- Adds `NonNilUuid` using `impl_reflect_opaque!`.

## Testing

- Built with no issues found locally.
- Essentially the same as the `Uuid` support except without `Default`.

Co-authored-by: TM Storey <mail@tmstorey.id.au>
2025-05-04 08:22:57 +00:00
ickshonpe
5e2ecf4178
Text background colors (#18892)
# Objective

Add background colors for text.

Fixes #18889

## Solution

New component `TextBackgroundColor`, add it to any UI `Text` or
`TextSpan` entity to add a background color to its text.
New field on `TextLayoutInfo` `section_rects` holds the list of bounding
rects for each text section.

The bounding rects are generated in `TextPipeline::queue_text` during
text layout, `extract_text_background_colors` extracts the colored
background rects for rendering.

Didn't include `Text2d` support because of z-order issues.

The section rects can also be used to implement interactions targeting
individual text sections.

## Testing
Includes a basic example that can be used for testing:
```
cargo run --example text_background_colors
```
---

## Showcase


![tbcm](https://github.com/user-attachments/assets/e584e197-1a8c-4248-82ab-2461d904a85b)

Using a proportional font with kerning the results aren't so tidy (since
the bounds of adjacent glyphs can overlap) but it still works fine:


![tbc](https://github.com/user-attachments/assets/788bb052-4216-4019-a594-7c1b41164dd5)

---------

Co-authored-by: Olle Lukowski <lukowskiolle@gmail.com>
Co-authored-by: Gilles Henaux <ghx_github_priv@fastmail.com>
2025-05-04 08:18:46 +00:00
Martín Maita
8c34cbbb27
Add TextureAtlas convenience methods (#19023)
# Objective

- Add a few useful methods to `TextureAtlas`.

## Solution

- Added `TextureAtlas::with_index()`.
- Added `TextureAtlas::with_layout()`.

## Testing

- CI checks.
2025-05-04 08:11:59 +00:00
Taj Holliday
2affecdb07
Audio sink seek adopted (#18971)
Adopted #13869 

# Objective

Fixes #9076 

## Solution

Using `rodio`'s `try_seek`

## Testing

@ivanstepanovftw added a `seek` system using `AudioSink` to the
`audio_control.rs` example. I got it working with .mp3 files, but rodio
doesn't support seeking for .ogg and .flac files, so I removed it from
the commit (since the assets folder only has .ogg files). Another thing
to note is that `try_seek` fails when using `PlaybackMode::Loop`, as
`rodio::source::buffered::Buffered` doesn't support `try_seek`. I
haven't tested `SpatialAudioSink`.

## Notes

I copied the docs for `try_seek` verbatim from `rodio`, and re-exported
`rodio::source::SeekError`. I'm not completely confident in those
decisions, please let me know if I'm doing anything wrong.

</details>

---------

Co-authored-by: Ivan Stepanov <ivanstepanovftw@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-05-03 11:29:38 +00:00
Peter S.
cd67bac544
Expose deferred screen edges setting for ios devices (#18729)
# Objective

- This just exposes the preferred [screen edges deferring system
gestures](https://developer.apple.com/documentation/uikit/uiviewcontroller/preferredscreenedgesdeferringsystemgestures)
setting from
[winit](https://docs.rs/winit/latest/winit/platform/ios/trait.WindowExtIOS.html#tymethod.set_preferred_screen_edges_deferring_system_gestures),
making it accessible in bevy apps.

This setting is useful for ios apps that make use of the screen edges,
letting the app have control of the first edge gesture before relegating
to the os.


## Testing

- Tested on simulator and on an iPhone Xs

---

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-04-30 21:24:53 +00:00
ickshonpe
21b62d640b
Change the default visual box for OverflowClipMargin to PaddingBox (#18935)
# Objective

The default should be `OverflowClipBox::PaddingBox` not
`OverflowClipBox::ContentBox`

`padding-box` is the default in CSS. 

## Solution

Set the default to `PaddingBox`.

## Testing

Compare the `overflow` UI example on main vs with this PR. You should
see that on main the outline around the inner node gets clipped. With
this PR by default clipping starts at the inner edge of the border (the
`padding-box`) and the outlines are visible.

Fixes #18934
2025-04-30 21:00:42 +00:00
Brezak
3631a64a3d
Add a method to clear all related entity to EntityCommands and friends (#18907)
# Objective

We have methods to:
- Add related entities
- Replace related entities
- Remove specific related entities

We don't have a method the remove all related entities so.

## Solution

Add a method to remove all related entities.

## Testing

A new test case.
2025-04-30 20:59:29 +00:00
Rob Parrett
b40845d296
Fix a few subcrate import paths in examples (#19002)
# Objective

Tripped over the `directional_navigation` one recently while playing
around with that example.

Examples should import items from `bevy` rather than the sub-crates
directly.

## Solution

Use paths re-exported by `bevy`.

## Testing

```
cargo run --example log_diagnostics
cargo run --example directional_navigation
cargo run --example custom_projection
```
2025-04-30 18:41:17 +00:00
François Mockers
0fa115f911
fix new nightly lint on mikktspace (#18988)
# Objective

- new nightly lint make CI fail

## Solution

- Follow the lint: https://github.com/rust-lang/rust/pull/123239
2025-04-30 05:19:01 +00:00
1.11e-1f64
9fca353782
Make AccessConflicts::is_empty public (#18688)
# Objective

When implementing `SystemParam` for an object which contains a mutable
reference to World, which cannot be derived due to a required lifetime
parameter, it's necessary to check that there aren't any conflicts.

As far as I know, the is_empty method is the only way provided to check
for no conflicts at all
2025-04-28 21:48:46 +00:00
Frédéric Vauchelles
19682aa4c3
Added derive Reflect to UntypedHandle and UntypedAssetId (#18827)
# Objective

- We have the ability to serialize/deserialize `Handle<T>` with
[`TypedReflectSerializer`](https://docs.rs/bevy/latest/bevy/reflect/serde/struct.TypedReflectSerializer.html)
and
[`TypedReflectDeserializer`](https://docs.rs/bevy/latest/bevy/reflect/serde/struct.TypedReflectDeserializer.html),
but it is not possible for `UntypedHandle`.
- `Handle<T>` already has the `Reflect` derive, so it sounds coherent to
also have this derive also on the untyped API

## Solution

- Add the `Reflect` derive macro to both `UntypedHandle` and `
UntypedAssetId`.

## Testing

- I used a custom processor to handle the serialization based on the
example of
[`TypedReflectSerializer`](https://docs.rs/bevy/latest/bevy/reflect/serde/struct.TypedReflectSerializer.html)
(see [source
code](https://docs.rs/bevy_reflect/0.15.3/src/bevy_reflect/serde/ser/serializer.rs.html#149))

Co-authored-by: Frédéric Vauchelles <frederic.vauchelles@outlook.com>
2025-04-28 21:46:36 +00:00
mhsalem36
2556405feb
Fixes #15389, added docs to RawHandleWrapper::_window field (#18832)
# Objective

- Fixes https://github.com/bevyengine/bevy/issues/15389.
- Add documentation for RawHandleWrapper::_window field since It's
needed to drop the window at the correct time.

## Solution

- Added documentation to RawHandleWrapper::_window field as same as
WindowWrapper documentation.

## Testing

- No testing needed since it is documentation. 

---
2025-04-28 21:44:57 +00:00
JoshValjosh
7f9eae2c87
Fully qualify crate paths in BevyManifest (#18938)
# Objective

Subtle, very minor issue. The following code fails to compile on main:
```rs
struct bevy;
#[derive(::bevy::ecs::component::Component)]
struct MyComponent;
```
The derive proc macro is pasting in essentially:
```rs
impl bevy::ecs::component::Component for MyComponent
```
...which normally works, but I've added `struct bevy`, which makes the
proc macro spit out incorrect code.

Very cursed, but to my knowledge has never been encountered in practice.
All the same, it's technically incorrect and should be fixed.

## Solution

The solution is simply to prepend `::` to crate names. Specifically, all
(all?) Bevy's derive macros determine the root crate name using
`BevyManifest`, which does some toml-parsing witchcraft to figure out
whether to qualify names using the umbrella `bevy` crate or individual
`bevy_xxx` crates. I just added a `::` to the spot where we parse the
`syn::Path`. The above example compiles properly after that.

## Testing

- CI should catch any errors since this change should cause compile
errors if for some reason they're being used in a cursed way somewhere
that would make this break something.

## Note

If this does break something for someone, this *really* needs a comment
in `BevyManifest::maybe_get_path` explaining why we can't make this
change.
2025-04-28 21:43:48 +00:00
Gino Valente
31e9a3c411
bevy_reflect: Re-reflect hashbrown types (#18944)
# Objective

Fixes #18943

## Solution

Reintroduces support for `hashbrown`'s `HashMap` and `HashSet` types.
These were inadvertently removed when `bevy_platform` newtyped the
`hashbrown` types.

Since we removed our `hashbrown` dependency, I gated these impls behind
a `hashbrown` feature. Not entirely sure if this is necessary since we
enabled it for `bevy_reflect` through `bevy_platform` anyways. (Complex
features still confuse me a bit so let me know if I can just remove it!)

I also went ahead and preemptively implemented `TypePath` for `PassHash`
while I was here.

## Testing

You can test that it works by adding the following to a Bevy example
based on this PR (you'll also need to include `hashbrown` of course):

```rust
#[derive(Reflect)]
struct Foo(hashbrown::HashMap<String, String>);
```

Then check it compiles with:

```
cargo check --example hello_world --no-default-features --features=bevy_reflect/hashbrown
```
2025-04-28 19:26:53 +00:00
Krzysztof Żywiecki
7f0490655c
Removed conversion from pointer physical coordinates to viewport local coordinates in bevy_picking make_ray function (#18870)
# Objective

- Fixes #18856.

## Solution

After PR #17633, `Camera::viewport_to_world` method corrects
`viewport_position` passed in that input so that it's offset by camera's
viewport. `Camera::viewport_to_world` is used by `make_ray` function
which in turn also offsets pointer position by viewport position, which
causes picking objects to be shifted by viewport position, and it wasn't
removed in the aforementioned PR. This second offsetting in `make_ray`
was removed.

## Testing

- I tested simple_picking example by applying some horizontal offset to
camera's viewport.
- I tested my application that displayed a single rectangle with picking
on two cameras arranged in a row. When using local bevy with this fix,
both cameras can be used for picking correctly.
- I modified split_screen example: I added observer to ground plane that
changes color on hover, and removed UI as it interfered with picking
both on master and my branch. On master, only top left camera was
triggering the observer, and on my branch all cameras could change
plane's color on hover.
- I added viewport offset to mesh_picking, with my changes it works
correctly, while on master picking ray is shifted.
- Sprite picking with viewport offset doesn't work both on master and on
this branch.

These are the only scenarios I tested. I think other picking functions
that use this function should be tested but I couldn't track more uses
of it.

Co-authored-by: Krzysztof Zywiecki <krzysiu@pop-os.Dlink>
2025-04-27 13:54:28 +00:00
Martín Maita
17da7e13f2
Update spin requirement from 0.9.8 to 0.10.0 (#18655)
# Objective

- Closes #18643 

## Solution

- Updated spin requirement from 0.9.8 to 0.10.0.
- Renamed `spin/portable_atomic` feature to `spin/portable-atomic`.

## Testing

- CI checks (already passing in the dependabot PR).

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-27 06:18:24 +00:00
Mathis Brossier
119eb51f00
Fix game_of_life shader relying on Naga bug (#18951)
# Objective

The game of life example shader relies on a Naga bug
([6397](https://github.com/gfx-rs/wgpu/issues/6397) /
[4536](https://github.com/gfx-rs/wgpu/issues/4536)). In WGSL certain
arithmetic operations must be explicitly parenthesized
([reference](https://www.w3.org/TR/WGSL/#operator-precedence-associativity)).
Naga doesn't enforce that (and also the precedence order is [messed
up](https://github.com/gfx-rs/wgpu/issues/4536#issuecomment-1780113990)).

So this example may break soon. This is the only sample shader having
this issue.

## Solution

added parentheses

## Testing

ran the example before and after the fix with `cargo run --example
compute_shader_game_of_life`
2025-04-26 21:38:08 +00:00
Kristoffer Søholm
78d9f05d31
Update cosmic-text to 0.14 (#18651)
# Objective

New cosmic-text version is out, we should use it.

## Solution

Bump the version.

## Testing

Code compiles, text example works
2025-04-26 21:36:24 +00:00
Olle Lukowski
9167f02bdf
Create EntityCommands::remove_if (#18899)
# Objective

Fixes #18857.

## Solution

Add the requested method, and a `try_` variant as well.

## Testing

It compiles, doctests succeed, and is trivial enough that I don't think
it needs a unit test (correct me if I'm wrong though).
2025-04-26 21:32:02 +00:00
ickshonpe
2b7cecd5b3
FontAtlasSet fixes (#18850)
# Objective

Fix problems with  `FontAtlasSet`:

* `FontAtlasSet` derives `Asset` but `FontAtlasSet`s are not Bevy
assets.
* The doc comments state that `FontAtlasSet`s are assets that are
created automatically when fonts are loaded. They aren't, they are
created as needed during text updates.

## Solution
* Removed the `Asset` derive.
* Rewrote the doc comments.
2025-04-26 21:22:40 +00:00
Raul Rabadan Arroyo
c4408a817b
Make the example android app explicitly handle some device config change events to avoid getting the activity destroyed (#18839)
# Objective

Fixes #18316

## Solution

Add android:configChanges="orientation|screenSize" to the
AndroidManifest.xml as indicated in https://stackoverflow.com/a/3329486
to avoid the GameActivity from getting destroyed, making the app
stuck/crash.

## Testing

I checked the results in my phone and after adding the config change the
issue went away. The change is relatively trivial, so there shouldn't be
the need to make a lot more testing.
2025-04-26 21:21:50 +00:00
JoshValjosh
9cf1a1afa2
Remove unused query param (#18924)
# Objective

Was copying off `bevy_ui`'s homework writing a picking backend and
noticed the `Has<IsDefaultPickingCamera>` is not used anywhere.

## Testing

Ran a random example.

This shouldn't cause any behavioral changes at all because the
component/archetype access/filter flags should be the same. `Has<X>`
doesn't affect access since it doesn't actually read or write anything,
and it doesn't affect matched archetypes either. Can't think of another
reason any behavior would change.
2025-04-26 21:17:14 +00:00
Rahmat Nazali Salimi
26f0ce272e
Fix minor typo on bevy_ecs example (#18926)
# Objective

A small typo was found on `bevy_ecs/examples/event.rs`.
I know it's very minor but I'd think fixing it would still help others
in the long run.

## Solution

Fix the typo.

## Testing

I don't think this is necessary.
2025-04-26 21:16:09 +00:00
François Mockers
12f71a8936
don't overflow when relations are empty (#18891)
# Objective

- Fixes #18890 

## Solution

- Don't overflow when substracting, bound at 0

## Testing

- Reproducer from the issue now works
2025-04-21 20:38:43 +00:00
charlotte
18e1bf1c3d
Swap order of eviction/extraction when extracting for specialization (#18846)
# Objective

Fixes #18843 

## Solution

We need to account for the material being added and removed in the
course of the same frame. We evict the caches first because the entity
will be re-added if it was marked as needing specialization, which
avoids another check on removed components to see if it was "really"
despawned.
2025-04-15 06:44:01 +00:00
charlotte
6eaa6a6a03
Revert attempt to fix memory leak (#18845)
This reverts commit a9b0b4e7f7.
2025-04-15 01:57:53 +00:00
Carter Anderson
f3f6cad43c
Panic on overlapping one-to-one relationships (#18833)
# Objective

One to one relationships (added in
https://github.com/bevyengine/bevy/pull/18087) can currently easily be
invalidated by having two entities relate to the same target.

Alternative to #18817 (removing one-to-one relationships)

## Solution

Panic if a RelationshipTarget is already targeted. Thanks @urben1680 for
the idea!

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
2025-04-15 01:25:37 +00:00
Patrick Walton
56784de769
Fix the ordering of the systems introduced in #18734. (#18825)
There's still a race resulting in blank materials whenever a material of
type A is added on the same frame that a material of type B is removed.
PR #18734 improved the situation, but ultimately didn't fix the race
because of two issues:

1. The `late_sweep_material_instances` system was never scheduled. This
PR fixes the problem by scheduling that system.

2. `early_sweep_material_instances` needs to be called after *every*
material type has been extracted, not just when the material of *that*
type has been extracted. The `chain()` added during the review process
in PR #18734 broke this logic. This PR reverts that and fixes the
ordering by introducing a new `SystemSet` that contains all material
extraction systems.

I also took the opportunity to switch a manual reference to
`AssetId::<StandardMaterial>::invalid()` to the new
`DUMMY_MESH_MATERIAL` constant for clarity.

Because this is a bug that can affect any application that switches
material types in a single frame, I think this should be uplifted to
Bevy 0.16.
2025-04-14 21:17:48 +00:00
Carter Anderson
59bdaca29d
Revert "Allow partial support for bevy_log in no_std (#18782)" (#18816)
This reverts commit ac52cca033.

Fixes #18815

# Objective

#18782 resulted in using `log` macros instead of `tracing` macros (in
the interest of providing no_std support, specifically no_atomic
support). That tradeoff isn't worth it, especially given that tracing is
likely to get no_atomic support.

## Solution

Revert #18782
2025-04-14 21:15:01 +00:00
JaySpruce
045ef4c307
Add remove_children and remove_related to EntityWorldMut and EntityCommands (#18835)
Fixes #18834.

`EntityWorldMut::remove_children` and `EntityCommands::remove_children`
were removed in the relationships overhaul (#17398) and never got
replaced.

I don't *think* this was intentional (the methods were never mentioned
in the PR or its comments), but I could've missed something.
2025-04-14 20:27:08 +00:00
Patrick Walton
5e69518f10
Don't restrict the scene viewer to loading assets from approved paths. (#18828)
The purpose of the scene viewer is to load arbitrary glTF scenes, so
it's inconvenient if they have to be moved into the Bevy assets
directory first. Thus this patch switches the scene viewer to use
`UnapprovedPathMode::Allow`.

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-04-14 20:19:37 +00:00
Matty Weatherley
47f46b5bdf
Expose the output curve type in with_derivative (#18826)
# Objective

I was wrong about how RPITIT works when I wrote this stuff initially,
and in order to actually give people access to all the traits
implemented by the output (e.g. Debug and so on) it's important to
expose the real output type, even if it makes the trait uglier and less
comprehensible. (☹️)

## Solution

Expose the curve output type of the `CurveWithDerivative` trait and its
double-derivative companion. I also added a bunch of trait derives to
`WithDerivative<T>`, since I think that was just an oversight.
2025-04-14 20:18:00 +00:00
JaySpruce
e3384bb8f0
Fix wrong method call in relationship replacement command (#18824)
Fixes a small mix-up from #18058, which added bulk relationship
replacement methods.

`EntityCommands::replace_related_with_difference` calls
`EntityWorldMut::replace_children_with_difference` instead of
`EntityWorldMut::replace_related_with_difference`, which means it always
operates on the `ChildOf` relationship instead of the `R: Relationship`
generic it's provided.

`EntityCommands::replace_children_with_difference` takes an `R:
Relationship` generic that it shouldn't, but it accidentally works
correctly on `main` because it calls the above method.
2025-04-14 20:15:33 +00:00
Alice Cecile
9254297acd
Use never_say_never hack to work around Rust 2024 regression for fn traits (#18804)
# Objective

After #17967, closures which always panic no longer satisfy various Bevy
traits. Principally, this affects observers, systems and commands.

While this may seem pointless (systems which always panic are kind of
useless), it is distinctly annoying when using the `todo!` macro, or
when writing tests that should panic.

Fixes #18778.

## Solution

- Add failing tests to demonstrate the problem
- Add the trick from
[`never_say_never`](https://docs.rs/never-say-never/latest/never_say_never/)
to name the `!` type on stable Rust
- Write looots of docs explaining what the heck is going on and why
we've done this terrible thing

## To do

Unfortunately I couldn't figure out how to avoid conflicting impls, and
I am out of time for today, the week and uh the week after that.
Vacation! If you feel like finishing this for me, please submit PRs to
my branch and I can review and press the button for it while I'm off.

Unless you're Cart, in which case you have write permissions to my
branch!

- [ ] fix for commands
- [ ] fix for systems
- [ ] fix for observers
- [ ] revert https://github.com/bevyengine/bevy-website/pull/2092/

## Testing

I've added a compile test for these failure cases and a few adjacent
non-failing cases (with explicit return types).

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2025-04-14 19:59:48 +00:00
lcnr
d7ec6a90f2
remove reliance on a trait solver inference bug (#18840)
The parameter `In` of `call_inner` is completely unconstrained by its
arguments and return type. We are only able to infer it by assuming that
the only associated type equal to `In::Param<'_>` is `In::Param<'_>`
itself. It could just as well be some other associated type which only
normalizes to `In::Param<'_>`. This will change with the next-generation
trait solver and was encountered by a crater run
https://github.com/rust-lang/rust/pull/133502-

cc
https://github.com/rust-lang/trait-system-refactor-initiative/issues/168

I couldn't think of a cleaner alternative here. I first tried to just
provide `In` as an explicit type parameter. This is also kinda ugly as I
need to provide a variable number of them and `${ignore(..)}` is
currently still unstable https://github.com/rust-lang/rust/issues/83527.

Sorry for the inconvenience. Also fun that this function exists to avoid
a separate solver bug in the first place 😅
2025-04-14 19:55:31 +00:00
charlotte
6f3ea06060
Make sure the mesh actually exists before we try to specialize. (#18836)
Fixes #18809
Fixes #18823

Meshes despawned in `Last` can still be in visisible entities if they
were visible as of `PostUpdate`. Sanity check that the mesh actually
exists before we specialize. We still want to unconditionally assume
that the entity is in `EntitySpecializationTicks` as its absence from
that cache would likely suggest another bug.
2025-04-14 19:09:02 +00:00
charlotte
a9b0b4e7f7
Mark render assets as modified when removed from the asset server (#18814)
# Objective

Fixes #18808

## Solution

When an asset emits a removed event, mark it as modified in the render
world to ensure any appropriate bookkeeping runs as necessary.
2025-04-11 23:18:26 +00:00
Carter Anderson
e9a0ef49f9
Rename bevy_platform_support to bevy_platform (#18813)
# Objective

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.

## Solution

- Rename `bevy_platform_support` to `bevy_platform`.
2025-04-11 23:13:28 +00:00
François Mockers
88f863efed
don't disable std in bevy_dylib (#18807)
# Objective

- `bevy_dylib` currently doesn't build independently
```
cargo build -p bevy_dylib
   Compiling bevy_dylib v0.16.0-rc.4 (/crates/bevy_dylib)
error: no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait

error: `#[panic_handler]` function required, but not found

error: unwinding panics are not supported without std
  |
  = help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding
  = note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem

error: could not compile `bevy_dylib` (lib) due to 3 previous errors
```

## Solution

- remove `#![no_std]` from `bevy_dylib`

## Testing

- it builds now
2025-04-11 18:44:53 +00:00
Zachary Harrold
ac52cca033
Allow partial support for bevy_log in no_std (#18782)
# Objective

- Fixes #18781

## Solution

- Moved `LogPlugin` into its own file gated behind a new `tracing`
feature.
- Used `log` instead of `tracing` where possible.
- Exposed a new `tracing` feature in `bevy` which enables
`bevy_log/tracing`.
- Gated `LogPlugin` from `DefaultPlugins` on `tracing` feature.

## Testing

- CI

---

## Migration Guide

- If you were previously using `bevy_log` with default features
disabled, enable the new `std` and `tracing` features.
- If you were using `bevy` with the default features disabled, enable
the new `tracing` feature.

## Notes

Almost all of the diffs in this PR come from moving `LogPlugin` into its
own file. This just makes the PR less noisy, since the alternative is
excessive `#[cfg(feature = "tracing")]` directives all over the plugin.

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-04-11 01:44:38 +00:00
charlotte
24baf324d6
Allowlist mali drivers for gpu preprocessing support. (#18769)
Fixes #17591

Looking at the arm downloads page, "r48p0" is a version number that
increments, where rXX is the major version and pX seems to be a patch
version. Take the conservative approach here that we know gpu
preprocessing is working on at least version 48 and presumably higher.
The assumption here is that the driver_info string will be reported
similarly on non-pixel devices.
2025-04-11 00:03:54 +00:00
Alice Cecile
31bb878d6d
Fix system param validation for piped systems (#18785)
# Objective

- Piped systems are an edge case that we missed when reworking system
parameter validation.
- Fixes #18755.

## Solution

- Validate the parameters for both systems, ~~combining the errors if
both failed validation~~ by simply using an early out.
- ~~Also fix the same bug for combinator systems while we're here.~~

## Testing

I've added a large number of tests checking the behavior under various
permutations. These are separate tests, rather than one mega test
because a) it's easier to track down bugs that way and b) many of these
are `should_panic` tests, which will halt the evaluation of the rest of
the test!

I've also added a test for exclusive systems being pipeable because we
don't have one and I was very surprised that that works!

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-04-10 23:16:22 +00:00
Carter Anderson
2944f5e79d
Ignore RenderEntity during entity clones (#18798)
# Objective

Fixes #18795

## Solution

Ignore RenderEntity during entity clones
2025-04-10 20:46:34 +00:00
Gilles Henaux
e7558cd0da
Fix documentation: incorrect references to the Update schedule in ExitCondition (#18438)
# Objective

- The referenced `ScheduleLabel` for `OnPrimaryClosed` and `OnAllClosed`
in `ExitCondition` was incorrect

## Solution

- Changed `Update` to `PostUpdate`
2025-04-10 20:31:18 +00:00
BD103
6d4a9ad01c
Make some changes to the migration guide recommendations (#18794)
# Objective

- I've worked on the migration guide in the past, so I've written down
some of the conventions and styles I've used.

## Solution

Please read through the descriptions of each commit, which justify the
change! In summary:

- Remove headings from migration guide template by moving style guide to
`migration_guides.md`
- Use parentheses to signal method and function names (`my_func()`
instead of `my_func`)
- Change the guidelines on using bullet points so they're not used for
every single migration guide.
- Remove suggestion to use diff blocks, as they don't syntax-highlight
Rust code.

---------

Co-authored-by: Rob Parrett <robparrett@gmail.com>
Co-authored-by: Miles Silberling-Cook <NthTensor@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-04-10 19:25:56 +00:00
Greeble
25a8b9f9f9
Fix newline in PointLightShadowMap comment (#18791)
A clippy failure slipped into #18768, although I'm not sure why CI
didn't catch it.

```sh
> cargo clippy --version
clippy 0.1.85 (4eb161250e 2025-03-15)

> cargo run -p ci
...
error: empty line after doc comment
   --> crates\bevy_pbr\src\light\mod.rs:105:5
    |
105 | /     /// The width and height of each of the 6 faces of the cubemap.
106 | |
    | |_^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments
    = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]`
    = help: if the empty line is unintentional remove it
help: if the documentation should include the empty line include it in the comment
    |
106 |     ///
    |
```
2025-04-10 18:05:11 +00:00
Zachary Harrold
73f03327c2
Add ? (#18783)
# Objective

- Fixes #18780

## Solution

- Add `?`

## Testing

- CI

---

## Notes

_smol_
2025-04-10 00:27:09 +00:00
jf908
ea69012799
Small docs PR for PointLightShadowMap/DirectionalLightShadowMap (#18768)
# Objective

- Improve the docs for `PointLightShadowMap` and
`DirectionalLightShadowMap`

## Solution

- Add example for how to use `PointLightShadowMap` and move the
`DirectionalLightShadowMap` example from `DirectionalLight`.
- Match `PointLight` and `DirectionalLight` docs about shadows.
- Describe what `size` means.

---------

Co-authored-by: Robert Swain <robert.swain@gmail.com>
2025-04-09 22:23:21 +00:00
charlotte
ee7b624024
Fix forward decal depth_fade_factor. (#18772)
Fixes #18758
2025-04-09 22:00:28 +00:00
charlotte
e799625ea5
Add binned 2d/3d Wireframe render phase (#18587)
# Objective

Fixes #16896
Fixes #17737

## Solution

Adds a new render phase, including all the new cold specialization
patterns, for wireframes. There's a *lot* of regrettable duplication
here between 3d/2d.

## Testing

All the examples.

## Migration Guide
- `WireframePlugin` must now be created with
`WireframePlugin::default()`.
2025-04-09 21:34:53 +00:00
Patrick Walton
6c619397d5
Unify RenderMaterialInstances and RenderMeshMaterialIds, and fix an associated race condition. (#18734)
Currently, `RenderMaterialInstances` and `RenderMeshMaterialIds` are
very similar render-world resources: the former maps main world meshes
to typed material asset IDs, and the latter maps main world meshes to
untyped material asset IDs. This is needlessly-complex and wasteful, so
this patch unifies the two in favor of a single untyped
`RenderMaterialInstances` resource.

This patch also fixes a subtle issue that could cause mesh materials to
be incorrect if a `MeshMaterial3d<A>` was removed and replaced with a
`MeshMaterial3d<B>` material in the same frame. The problematic pattern
looks like:

1. `extract_mesh_materials<B>` runs and, seeing the
`Changed<MeshMaterial3d<B>>` condition, adds an entry mapping the mesh
to the new material to the untyped `RenderMeshMaterialIds`.

2. `extract_mesh_materials<A>` runs and, seeing that the entity is
present in `RemovedComponents<MeshMaterial3d<A>>`, removes the entry
from `RenderMeshMaterialIds`.

3. The material slot is now empty, and the mesh will show up as whatever
material happens to be in slot 0 in the material data slab.

This commit fixes the issue by splitting out `extract_mesh_materials`
into *three* phases: *extraction*, *early sweeping*, and *late
sweeping*, which run in that order:

1. The *extraction* system, which runs for each material, updates
`RenderMaterialInstances` records whenever `MeshMaterial3d` components
change, and updates a change tick so that the following system will know
not to remove it.

2. The *early sweeping* system, which runs for each material, processes
entities present in `RemovedComponents<MeshMaterial3d>` and removes each
such entity's record from `RenderMeshInstances` only if the extraction
system didn't update it this frame. This system runs after *all*
extraction systems have completed, fixing the race condition.

3. The *late sweeping* system, which runs only once regardless of the
number of materials in the scene, processes entities present in
`RemovedComponents<ViewVisibility>` and, as in the early sweeping phase,
removes each such entity's record from `RenderMeshInstances` only if the
extraction system didn't update it this frame. At the end, the late
sweeping system updates the change tick.

Because this pattern happens relatively frequently, I think this PR
should land for 0.16.
2025-04-09 21:32:10 +00:00
charlotte
f75078676b
Initialize pre-processing pipelines only when culling is enabled. (#18759)
Better fix for #18463 that still allows enabling mesh preprocessing on
webgpu.

Fixes #18463
2025-04-09 21:31:29 +00:00
Patrick Walton
065a95e0a1
Make the StandardMaterial bindless index table have a fixed size regardless of the features that are enabled. (#18771)
Due to the preprocessor usage in the shader, different combinations of
features could cause the fields of `StandardMaterialBindings` to shift
around. In certain cases, this could cause them to not line up with the
bindings specified in `StandardMaterial`. This resulted in #18104.

This commit fixes the issue by making `StandardMaterialBindings` have a
fixed size. On the CPU side, it uses the
`#[bindless(index_table(range(M..N)))]` feature I added to `AsBindGroup`
in #18025 to do so. Thus this patch has a dependency on #18025.

Closes #18104.

---------

Co-authored-by: Robert Swain <robert.swain@gmail.com>
2025-04-09 20:39:42 +00:00
Brian Reavis
bc259fad04
Fix get_render_pipeline_state / get_compute_pipeline_state panic (#18752)
This fixes a panic that occurs if one calls
`PipelineCache::get_render_pipeline_state(id)` or
`PipelineCache::get_compute_pipeline_state(id)` with a queued pipeline
id that has not yet been processed by `PipelineCache::process_queue()`.

```
thread 'Compute Task Pool (0)' panicked at [...]/bevy/crates/bevy_render/src/render_resource/pipeline_cache.rs:611:24:
index out of bounds: the len is 0 but the index is 20
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
2025-04-09 16:55:19 +00:00
charlotte
e8fd750274
Fix unbatchable meshes. (#18761)
# Objective

Fixes #18550.

Because bin state for unbatchable meshes wasn't being cleared each
frame, the buffer indices for unbatchable meshes would demote from
sparse to dense storage and aggressively leak memory, with all kinds of
weird consequences downstream, namely supplying invalid instance ranges
for render.

## Solution

Clear out the unbatchable mesh bin state when we start a new frame.
2025-04-09 15:35:33 +00:00
Patrick Walton
dc7c8f228f
Add bindless support back to ExtendedMaterial. (#18025)
PR #17898 disabled bindless support for `ExtendedMaterial`. This commit
adds it back. It also adds a new example, `extended_material_bindless`,
showing how to use it.
2025-04-09 15:34:44 +00:00
Freyja-moth
714b4a43d6
Change with_related to work with a Bundle and added with_relationships method (#18699)
# Objective

Fixes #18678

## Solution

Moved the current `with_related` method to `with_relationships` and
added a new `with_related` that uses a bundle.

I'm not entirely sold on the name just yet, if anyone has any ideas let
me know.

## Testing

I wasn't able to test these changes because it crashed my computer every
time I tried (fun). But there don't seem to be any tests that use the
old `with_related` method so it should be fine, hopefully

## Showcase

```rust
commands.spawn_empty()
    .with_related::<Relationship>(Name::new("Related thingy"))
    .with_relationships(|rel| {
        rel.spawn(Name::new("Second related thingy"));
    });
```

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2025-04-09 02:34:49 +00:00
Moony
0410212f3d
Expose the added tick for change detection, both getting and setting. (#18746)
# Objective

- Allow viewing and setting the added tick for change detection aware
data, to allow operations like checking if the value has been modified
since first being added, and spoofing that state (i.e. returning the
value to default in place without a remove/insert dance)

## Solution

- Added corresponding functions matching the existing `changed` API:
  - `fn added(&self) -> Tick`
  - `fn set_added(&mut self)`
  - `fn set_last_added(&mut self, last_added: Tick)`

Discussed on discord @
https://canary.discord.com/channels/691052431525675048/749335865876021248/1358718892465193060

## Testing

- Running the bevy test suite by.. making a PR, heck.
- No new tests were introduced due to triviality (i.e. I don't know what
to test about this API, and the corresponding API for `changed` is
similarly lacking tests.)

---------

Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net>
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-04-08 21:13:27 +00:00
ickshonpe
fb159f9846
Fix AccessKit node bounds (#18706)
# Objective

Fixes #18685

## Solution

* Don't apply the camera translation.
* Calculate the min and max bounds of the accessibility node rect taking
the UI translation relative to its center not the top-left corner.

## Testing

Install [NVDA](https://www.nvaccess.org/). In NVDA set `Preferences ->
Settings -> Vision -> Enable Highlighting`.

Then run bevy's `tab_navigation` example:
```
cargo run --example tab_navigation
```
If everything is working correctly, NVDA should draw a border around the
currently selected tab button:

![Screenshot 2025-04-07
130523](https://github.com/user-attachments/assets/07d9a795-5d55-4b61-9602-2e8917020245)
2025-04-08 01:42:02 +00:00
Jaso333
1b593ea8d4
clarified docs for bundle removal commands (#18754)
# Objective

Clarify information in the docs about the bundle removal commands.

## Solution

Added information about how the intersection of components are removed.
2025-04-07 22:34:59 +00:00
dependabot[bot]
ce5a82b2bf
Bump crate-ci/typos from 1.31.0 to 1.31.1 (#18744)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.31.0 to
1.31.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/releases">crate-ci/typos's
releases</a>.</em></p>
<blockquote>
<h2>v1.31.1</h2>
<h2>[1.31.1] - 2025-03-31</h2>
<h3>Fixes</h3>
<ul>
<li><em>(dict)</em> Also correct <code>typ</code> to
<code>type</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/blob/master/CHANGELOG.md">crate-ci/typos's
changelog</a>.</em></p>
<blockquote>
<h2>[1.31.1] - 2025-03-31</h2>
<h3>Fixes</h3>
<ul>
<li><em>(dict)</em> Also correct <code>typ</code> to
<code>type</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b1a1ef3893"><code>b1a1ef3</code></a>
chore: Release</li>
<li><a
href="9c8a2c384f"><code>9c8a2c3</code></a>
docs: Update changelog</li>
<li><a
href="12195d75fe"><code>12195d7</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1267">#1267</a>
from epage/type</li>
<li><a
href="d4dbe5f77b"><code>d4dbe5f</code></a>
fix(dict): Also correct typ to type</li>
<li>See full diff in <a
href="https://github.com/crate-ci/typos/compare/v1.31.0...v1.31.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=crate-ci/typos&package-manager=github_actions&previous-version=1.31.0&new-version=1.31.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-07 16:14:44 +00:00
Máté Homolya
a616ffa8ac
Web support for atmosphere (#18582)
# Objective

Add web support to atmosphere by gating dual source blending and using a
macro to determine the target platform.
The main objective of this PR is to ensure that users of Bevy's
atmosphere feature can also run it in a web-based context where WebGPU
support is enabled.

## Solution

- Make use of the `#[cfg(not(target_arch = "wasm32"))]` macro to gate
the dual source blending, as this is not (yet) supported in web
browsers.
- Rename the function `sample_sun_illuminance` to `sample_sun_radiance`
and move calls out of conditionals to ensure the shader compiles and
runs in both native and web-based contexts.
- Moved the multiplication of the transmittance out when calculating the
sun color, because calling the `sample_sun_illuminance` function was
causing issues in web. Overall this results in cleaner code and more
readable.

## Testing

- Tested by building a wasm target and loading it in a web page with
Vite dev server using `mate-h/bevy-webgpu` repo template.
- Tested the native build with `cargo run --example atmosphere` to
ensure it still works with dual source blending.

---

## Showcase

Screenshots show the atmosphere example running in two different
contexts:

<img width="1281" alt="atmosphere-web-showcase"
src="https://github.com/user-attachments/assets/40b1ee91-89ae-41a6-8189-89630d1ca1a6"
/>

---------

Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
2025-04-06 20:06:55 +00:00
Greeble
a1fd3a4c69
Remove WebGL padding from MotionBlur (#18727)
## Objective

The `MotionBlur` component exposes renderer internals. Users shouldn't
have to deal with this.

```rust
MotionBlur {
    shutter_angle: 1.0,
    samples: 2,
    #[cfg(all(feature = "webgl2", target_arch = "wasm32", not(feature = "webgpu")))]
    _webgl2_padding: Default::default(),
},
```

## Solution

The renderer now uses a separate `MotionBlurUniform` struct for its
internals. `MotionBlur` no longer needs padding.

I was a bit unsure about the name `MotionBlurUniform`. Other modules use
a mix of `Uniform` and `Uniforms`.

## Testing

```
cargo run --example motion_blur
```

Tested on Win10/Nvidia across Vulkan, WebGL/Chrome, WebGPU/Chrome.
2025-04-06 20:00:59 +00:00
Zachary Harrold
7892f9e3af
Newtype hashbrown (#18694)
# Objective

- Fixes #18690
- Closes [#2065](https://github.com/bevyengine/bevy-website/pull/2065)
- Alternative to #18691

The changes to the Hash made in #15801 to the
[BuildHasher](https://doc.rust-lang.org/std/hash/trait.BuildHasher.html)
resulted in serious migration problems and downgraded UX for users of
Bevy's re-exported hashmaps. Once merged, we need to go in and remove
the migration guide added as part of #15801.

## Solution

- Newtype `HashMap` and `HashSet` instead of type aliases
- Added `Deref/Mut` to allow accessing future `hashbrown` methods
without maintenance from Bevy
- Added bidirectional `From` implementations to provide escape hatch for
API incompatibility
- Added inlinable re-exports of all methods directly to Bevy's types.
This ensures `HashMap::new()` works (since the `Deref` implementation
wont cover these kinds of invocations).

## Testing

- CI

---

## Migration Guide

- If you relied on Bevy's `HashMap` and/or `HashSet` types to be
identical to `hashbrown`, consider using `From` and `Into` to convert
between the `hashbrown` and Bevy types as required.
- If you relied on `hashbrown/serde` or `hashbrown/rayon` features, you
may need to enable `bevy_platform_support/serialize` and/or
`bevy_platform_support/rayon` respectively.

---

## Notes

- Did not replicate the Rayon traits, users will need to rely on the
`Deref/Mut` or `From` implementations for those methods.
- Did not re-expose the `unsafe` methods from `hashbrown`. In most cases
users will still have access via `Deref/Mut` anyway.
- I have added `inline` to all methods as they are trivial wrappings of
existing methods.
- I chose to make `HashMap::new` and `HashSet::new` const, which is
different to `hashbrown`. We can do this because we default to a
fixed-state build-hasher. Mild ergonomic win over using
`HashMap::with_hasher(FixedHasher)`.
2025-04-06 17:52:49 +00:00
Greeble
2f80d081bc
Fix many_foxes + motion blur = crash on WebGL (#18715)
## Objective

Fix  #18714.

## Solution

Make sure `SkinUniforms::prev_buffer` is resized at the same time as
`current_buffer`.

There will be a one frame visual glitch when the buffers are resized,
since `prev_buffer` is incorrectly initialised with the current joint
transforms.

Note that #18074 includes the same fix. I'm assuming this smaller PR
will land first.

## Testing

See repro instructions in #18714. Tested on `animated_mesh`,
`many_foxes`, `custom_skinned_mesh`, Win10/Nvidia with Vulkan,
WebGL/Chrome, WebGPU/Chrome.
2025-04-06 17:51:22 +00:00
Hennadii Chernyshchyk
d82c359a5a
Add Default for all schedule labels (#18731)
# Objective

In `bevy_enhanced_input`, I'm trying to associate `Actions` with a
schedule. I can do this via an associated type on a trait, but there's
no way to construct the associated label except by requiring a `Default`
implementation. However, Bevy labels don't implement `Default`.

## Solution

Add `Default` to all built-in labels. I think it should be useful in
general.
2025-04-06 16:44:33 +00:00
Brian Reavis
5dcfa52297
Expose TextureFormatFeatureFlags, TextureFormatFeatures from wgpu (#18721)
# Objective

This PR exposes the wgpu types necessary to use the result of
`RenderAdapter::get_texture_format_features`:

```rust
use bevy::render::render_resource::TextureFormatFeatureFlags;
// ^ now available

let adapter = world.resource::<RenderAdapter>();
let flags = adapter.get_texture_format_features(TextureFormat::R32Float).flags;
let filtering = flags.contains(TextureFormatFeatureFlags::FILTERABLE);
```

## Solution

- Expose `TextureFormatFeatureFlags`, `TextureFormatFeatures` like other
wgpu types in bevy_render
2025-04-05 03:44:37 +00:00
person93
989b360fec
Add accessors to DynamicEnum for the DynamicVariant (#18693)
# Objective

- Closes https://github.com/bevyengine/bevy/issues/18692

## Solution

Add the methods as described
```rust
impl DynamicEnum {
    fn variant(&self) -> &DynamicVariant;
    fn variant_mut(&mut self) -> &mut DynamicVariant;
}
```
2025-04-05 02:33:00 +00:00
Greeble
5da64ddbee
Fix motion blur on skinned meshes (#18712)
## Objective

Fix motion blur not working on skinned meshes.

## Solution

`set_mesh_motion_vector_flags` can set
`RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN` after specialization has
already cached the material. This can lead to
`MeshPipelineKey::HAS_PREVIOUS_SKIN` never getting set, disabling motion
blur.

The fix is to make sure `set_mesh_motion_vector_flags` happens before
specialization.

Note that the bug is fixed in a different way by #18074, which includes
other fixes but is a much larger change.

## Testing

Open the `animated_mesh` example and add these components to the
`Camera3d` entity:

```rust
MotionBlur {
    shutter_angle: 5.0,
    samples: 2,
    #[cfg(all(feature = "webgl2", target_arch = "wasm32", not(feature = "webgpu")))]
    _webgl2_padding: Default::default(),
},
#[cfg(all(feature = "webgl2", target_arch = "wasm32", not(feature = "webgpu")))]
Msaa::Off,
```

Tested on `animated_mesh`, `many_foxes`, `custom_skinned_mesh`,
Win10/Nvidia with Vulkan, WebGL/Chrome, WebGPU/Chrome. Note that testing
`many_foxes` WebGL requires #18715.
2025-04-04 22:36:03 +00:00
Lucas Franca
78d5c50b50
Add PartialEq and Hash reflections for AnimationNodeIndex (#18718)
# Objective

Fixes #18701

## Solution

Add reflection of `PartialEq` and `Hash` to `AnimationNodeIndex`

## Testing

Added a new `#[test]` with the minimal reproduction posted on #18701.
2025-04-04 16:35:12 +00:00
ZoOL
06f9e5eca5
fix typo (#18696)
# Objective

- fix some typo

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-04-03 17:18:09 +00:00
BD103
4f2fa81cef
Add #[deprecated(since = "0.16.0", ...)] to items missing it (#18702)
# Objective

- The `#[deprecated]` attributes supports a `since` field, which
documents in which version an item was deprecated. This field is visible
in `rustdoc`.
- We inconsistently use `since` throughout the project.

For an example of what `since` renders as, take a look at
`ChildOf::get()`:

```rust
/// The parent entity of this child entity.
#[deprecated(since = "0.16.0", note = "Use child_of.parent() instead")]
#[inline]
pub fn get(&self) -> Entity {
    self.0
}
```


![image](https://github.com/user-attachments/assets/2ea5d8c9-2eab-430a-9a1c-421f315ff123)


## Solution

- Add `since = "0.16.0"` to all `#[deprecated]` attributes that do not
already use it.
- Add an example of deprecating a struct with the `since` field in the
migration guide document.

I would appreciate if this could be included in 0.16's release, as its a
low-risk documentation improvement that is valuable for the release, but
I'd understand if this was cut.

## Testing

You can use `cargo doc` to inspect the rendered form of
`#[deprecated(since = "0.16.0", ...)]`.
2025-04-03 17:06:01 +00:00
Vic
6a7fc9ce4b
use entity set collections type aliases instead of defaults (#18695)
# Objective

Newest installment of the #16547 series.

In #18319 we introduced `Entity` defaults to accomodate the most common
use case for these types, however that resulted in the switch of the `T`
and `N` generics of `UniqueEntityArray`.
Swapping generics might be somewhat acceptable for `UniqueEntityArray`,
it is not at all acceptable for map and set types, which we would make
generic over `T: EntityEquivalent` in #18408.

Leaving these defaults in place would result in a glaring inconsistency
between these set collections and the others.

Additionally, the current standard in the engine is for "entity" to mean
`Entity`. APIs could be changed to accept `EntityEquivalent`, however
that is a separate and contentious discussion.

## Solution

Name these set collections `UniqueEntityEquivalent*`, and retain the
`UniqueEntity*` name for an alias of the `Entity` case.
While more verbose, this allows for all generics to be in proper order,
full consistency between all set types*, and the "entity" name to be
restricted to `Entity`.
On top of that, `UniqueEntity*` now always have 1 generic less, when
previously this was not enforced for the default case.

*`UniqueEntityIter<I: Iterator<T: EntityEquivalent>>` is the sole
exception to this. Aliases are unable to enforce bounds
(`lazy_type_alias` is needed for this), so for this type, doing this
split would be a mere suggestion, and in no way enforced.
Iterator types are rarely ever named, and this specific one is intended
to be aliased when it sees more use, like we do for the corresponding
set collection iterators.
Furthermore, the `EntityEquivalent` precursor `Borrow<Entity>` was used
exactly because of such iterator bounds!
Because of that, we leave it as is.

While no migration guide for 0.15 users, for those that upgrade from
main:
`UniqueEntityVec<T>` -> `UniqueEntityEquivalentVec<T>`
`UniqueEntitySlice<T>` -> `UniqueEntityEquivalentSlice<T>`
`UniqueEntityArray<N, T>` -> `UniqueEntityEquivalentArray<T, N>`
2025-04-03 03:59:04 +00:00
Periwink
bd00c915ec
add reflect for SocketAddr (#18676) 2025-04-03 03:51:20 +00:00
Tim Overbeek
e02c3662fb
Code quality cleanup pass for #[require] (#18621)
#18555 improved syntax for required components.

However some code was a bit redundant after the new parsing and struct
initializing would not give proper errors.
This PR fixes that.

---------

Co-authored-by: Tim Overbeek <oorbecktim@Tims-MacBook-Pro.local>
2025-04-02 20:09:04 +00:00
Chris Russell
9e240ee99a
Improve error message for missing events (#18683)
# Objective

Improve the parameter validation error message for
`Event(Reader|Writer|Mutator)`.

System parameters defined using `#[derive(SystemParam)]`, including the
parameters for events, currently propagate the validation errors from
their subparameters. The error includes the type of the failing
parameter, so the resulting error includes the type of the failing
subparameter instead of the derived parameter.

In particular, `EventReader<T>` will report an error from a
`Res<Events<T>>`, even though the user has no parameter of that type!

This is a follow-up to #18593.

## Solution

Have `#[derive]`d system parameters map errors during propagation so
that they report the outer parameter type.

To continue to provide context, add a field to
`SystemParamValidationError` that identifies the subparameter by name,
and is empty for non-`#[derive]`d parameters.

Allow them to override the failure message for individual parameters.
Use this to convert "Resource does not exist" to "Event not initialized"
for `Event(Reader|Writer|Mutator)`.

## Showcase

The validation error for a `EventReader<SomeEvent>` parameter when
`add_event` has not been called changes from:

Before: 
```
Parameter `Res<Events<SomeEvent>>` failed validation: Resource does not exist
```

After
```
Parameter `EventReader<SomeEvent>::events` failed validation: Event not initialized
```
2025-04-02 19:25:48 +00:00
BD103
746b593833
Fix indentation of bevy/query strict parameter in docs (#18681)
# Objective

- The `strict` field of
[`BrpQueryParams`](https://dev-docs.bevyengine.org/bevy/remote/builtin_methods/struct.BrpQueryParams.html)
was newly added as part of 0.16.
- Its documentation in `lib.rs` improperly indents `strict`, making look
like its part of
[`BrpQueryFilter`](https://dev-docs.bevyengine.org/bevy/remote/builtin_methods/struct.BrpQueryFilter.html):


![image](https://github.com/user-attachments/assets/f49521da-36d3-4d5d-a7ea-f7a44ddaf195)

## Solution

- Fix `strict`'s indentation so its clear that it is a field of
`BrpQueryParams`, not `BrpQueryFilter`.

I would like this to be included in 0.16, since it's a trivial
documentation change that fixes an error, but if it needs to be removed
from the milestone that's fine.

## Testing

Run `cargo doc -p bevy_remote --no-deps` and verify the indentation is
fixed. :)
2025-04-02 17:33:02 +00:00
JaySpruce
6fc31bc623
Implement insert_children for EntityCommands (#18675)
Extension of #18409.

I was updating a migration guide for hierarchy commands and realized
`insert_children` wasn't added to `EntityCommands`, only
`EntityWorldMut`.

This adds that and `insert_related` (basically just some
copy-and-pasting).
2025-04-02 17:31:29 +00:00
Kristoffer Søholm
86fa2ac570
Fix issues in log_diagnostics example (#18652)
# Objective

Adopts / builds on top of #18435.

The `log_diagnostics` example has bit-rotted and accumulated multiple
issues:
- It didn't explain the ordering constraint on DefaultPlugins, as noted
by the original PR
- Apparently `AssetCountDiagnosticsPlugin` no longer exists (?!). I
couldn't figure out when or why it was removed, maybe it got missed in
Assets v2?
- The comments didn't explain what kind of info you get by the various
plugins, making you do work to figure it out
- ~As far as I can tell `RenderDiagnosticsPlugin` currently doesn't
register any diagnostics in the traditional sense, but is only focused
on rendering spans? At least it doesn't print anything extra when added
for me, so having it here is misleading.~ It didn't print anything
because there was nothing to render in this example

## Solution

- Make all plugins be commented in to prevent further bit-rot
- Remove reference to the missing plugin
- Add extra comments describing the diagnostics in more detail
- Add something to render so we get render diagnostics

## Testing

Run the example, see relevant diagnostics printed out
2025-04-02 16:59:50 +00:00
Martín Maita
5973ba418f
Bump crate-ci/typos from 1.30.2 to 1.31.0 (#18656)
# Objective

- Fixes #18642

## Solution

- Bumped crate-ci/typos from 1.30.2 to 1.31.0.
- Fixed typos.

## Testing

- Typos were fixed.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 10:59:15 +00:00
Carter Anderson
d8fa57bd7b
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.
2025-04-02 00:10:10 +00:00
JaySpruce
34f1159761
Update relationship commands to use EntityCommands instead of Commands (#18667)
These should use `EntityCommands` so that the entity existence check is
hooked up to the default error handler, rather than only panicking.
2025-04-01 20:58:06 +00:00
ickshonpe
17435c7118
Remove the visited local system param from update_ui_context_system. (#18664)
# Objective

The `visited: Local<HashSet<Entity>>` system param is meant to track
which entities `update_contexts_recursively` has visited and updated but
when the reparent_nodes_query isn't ordered descending from parent to
child nodes can get marked as visited even though their camera target is
unset and if the camera target is unset then the node won't be rendered.

Fixes #18616

## Solution

Remove the `visited` system param from `update_ui_context_system` and
the associated visited check from `update_contexts_recursively`. It was
redundant anyway since the set_if_neq check is sufficient to track
already updated nodes.

## Testing

The example from #18616 can be used for testing.
2025-04-01 19:49:39 +00:00
Chris Russell
9daf4e7c8b
Include SystemParamValidationError in RunSystemError and RegisteredSystemError (#18666)
# Objective

Provide more useful errors when `World::run_system` and related methods
fail parameter validation.

Let callers determine whether the validation failure would have skipped
or failed the system.

Follow-up to #18541.

## Solution

Add a `SystemParamValidationError` value to the
`RunSystemError::InvalidParams` and
`RegisteredSystemError::InvalidParams` variants. That includes the
complete context of the parameter validation error, including the
`skipped` flag.
2025-04-01 19:27:08 +00:00
Zachary Harrold
61f3b3e8f5
Add Advice for Relocations to Migration Guide Template (#18654)
# Objective

Relocations are a fairly common form of migration, and a tabular format
can make reading an otherwise repetitive list much easier. This should
be included in the migration guide template.

## Solution

- Added a dot-point highlighting a tabular format for relocations, with
an explanation.
- Added a dot-point indicating migrations should be written WRT the
currently published version of a crate.
- Fixed some formatting in an adjacent dot-point.

## Testing

- CI
2025-04-01 17:18:59 +00:00
JaySpruce
cbc023b3bb
Add notes to fallible commands (#18649)
Follow-up to #18639.

Fallible commands should have notes explaining how they can fail, what
error they return, and how it's handled.
2025-04-01 02:51:53 +00:00
Eagster
f5250dbb50
Finish #17558, re-adding insert_children (#18409)
fixes #17478

# Objective

- Complete #17558.
- the `insert_children` method was previously removed, and as #17478
points out, needs to be added back.

## Solution

- Add a `OrderedRelationshipSourceCollection`, which allows sorting,
ordering, rearranging, etc of a `RelationshipSourceCollection`.
- Implement `insert_related`
- Implement `insert_children`
- Tidy up some docs while I'm here.

## Testing

@bjoernp116 set up a unit test, and I added a doc test to
`OrderedRelationshipSourceCollection`.

---------

Co-authored-by: bjoernp116 <bjoernpollen@gmail.com>
Co-authored-by: Dmytro Banin <banind@cs.washington.edu>
Co-authored-by: Talin <viridia@gmail.com>
2025-04-01 02:21:09 +00:00
Eagster
5db67f35e4
Get names of queued components (#18451)
# Objective

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

## Solution

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

## Testing

Existing tests.

## Migration Guide

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

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

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-03-31 23:22:33 +00:00
Zachary Harrold
99289ad988
Add sleep based on spin to bevy_platform_support (#18633)
# Objective

- Fixes #18617

## Solution

- Added `thread::sleep` to `bevy_platform_support` using a spin-based
fallback.
- Fixed bug in `bevy_platform_support::time::Instant::elapsed`
(comparison was backwards)
- Switched `ScheduleRunnerPlugin` to use
`bevy_platform_support:🧵:sleep` on `std` and `no_std` platforms
(WASM + Browser excluded)

## Testing

- Ran reproduction code from @mockersf in linked issue and confirmed a
consistent 60 counts per `println!`.

---

## Notes

- I chose to add `bevy_platform_support:🧵:sleep` instead of
putting the fix in-line within `ScheduleRunnerPlugin` to keep the
separation of concerns clean. `sleep` is only used in one other location
in Bevy, `bevy_asset`, but I have decided to leave that as-is since
`bevy_asset` isn't `no_std` compatible anyway.
- The bug in `bevy_platform_support::time::Instant::elapsed` wasn't the
cause of this issue, but it did prevent this fix from working so I have
included the it in this PR.
2025-03-31 23:21:49 +00:00
JaySpruce
951c4dac7e
bevy_ecs/system/commands/ folder docs pass (#18639)
- Lots of nits, formatting, and rephrasing, with the goal of making
things more consistent.
- Fix outdated error handler explanation in `Commands` and
`EntityCommands` docs.
- Expand docs for system-related commands.
- Remove panic notes if the command only panics with the default error
handler.
- Update error handling notes for `try_` variants.
- Hide `prelude` import in most doctest examples, unless the example
uses something that people might not realize is in the prelude (like
`Name`).
- Remove a couple doctest examples that (in my opinion) didn't make
sense.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-03-31 19:26:58 +00:00
charlotte
bd5fed9050
Fix no indirect drawing (#18628)
# Objective

The `NoIndirectDrawing` wasn't working and was causing the scene not to
be rendered.

## Solution

Check the configured preprocessing mode when adding new batch sets and
mark them as batchable instead of muli-drawable if indirect rendering
has been disabled.

## Testing

`cargo run --example many_cubes -- --no-indirect-drawing`
2025-03-31 19:20:57 +00:00
Zachary Harrold
45e2398b51
Upgrade to Glam 0.29.3 and Simplify Feature Gating (#18638)
# Objective

- Fixes #18397
- Supersedes #18474
- Simplifies 0.16 migration

## Solution

- Upgrade to Glam 0.29.3, which has backported the `nostd-libm` feature.
- Expose a similar feature in `bevy_math` and enable it in
`bevy_internal`, allowing `bevy_math`, `bevy_input`, and
`bevy_transform` to be unconditional dependencies again.

## Testing

- CI

---

## Notes

- This includes `libm` as a dependency, but this was already the case in
the common scenario where `rand` or many other features were enabled.
Considering `libm` is an official Rust crate, it's a very low-risk
dependency to unconditionally include.
- For users who do not want `libm` included, simply import Bevy's
subcrates directly, since `bevy_math/nostd-libm` will not be enabled.
- I know we are _very_ late in the RC cycle for 0.16, but this has a
substantial impact on the usability of `bevy` that I consider worth
including.
2025-03-31 18:54:46 +00:00
Beni Bachmann
7826b94e06
Return triangle index instead of vertex index (Fixes #18081) (#18647)
# Objective

- Fixes #18081
- Enable use-cases like getting UVs or texture colors for the hit point
(which are currently not possible due to this bug).

## Solution

- Return the triangle index instead of the first vertex index of the
triangle.

## Testing

Tested successfully with my project which does a raycast to get the UV
coordinates of the hit. My code:
```rust
fn get_uv(
    mesh: &Mesh,
    attribute: &MeshVertexAttribute,
    hit: &RayMeshHit,
    _gizmos: &mut Gizmos,
) -> Result<Vec2> {
    let (a, b, c) = get_indices(mesh, hit)?;

    let attrs = mesh
        .attribute(*attribute)
        .ok_or_eyre(format!("Attribute {:?} not found", &attribute))?;
    let all_uvs: &Vec<[f32; 2]> = match &attrs {
        VertexAttributeValues::Float32x2(positions) => positions,
        _ => bail!("Unexpected types in {:?}", Mesh::ATTRIBUTE_UV_0),
    };

    let bary = hit.barycentric_coords;

    Ok(Vec2::from_array(all_uvs[a]) * bary.x
        + Vec2::from_array(all_uvs[b]) * bary.y
        + Vec2::from_array(all_uvs[c]) * bary.z)
}

fn get_indices(mesh: &Mesh, hit: &RayMeshHit) -> Result<(usize, usize, usize)> {
    let i = hit
        .triangle_index
        .ok_or_eyre("Intersection Index not found")?;

    Ok(mesh.indices().map_or_else(
        || (i, i + 1, i + 2),
        |indices| match indices {
            Indices::U16(indices) => (
                indices[i * 3] as usize,
                indices[i * 3 + 1] as usize,
                indices[i * 3 + 2] as usize,
            ),
            Indices::U32(indices) => (
                indices[i * 3] as usize,
                indices[i * 3 + 1] as usize,
                indices[i * 3 + 2] as usize,
            ),
        },
    ))
}
```

PS: created a new PR because the old one was coming from and targeting
the wrong branches
2025-03-31 18:50:15 +00:00
Lucas Franca
bbd458c813
Make bindings behind pbr_specular_textures flag consistent with other gated fields (#18645)
# Objective

Make all feature gated bindings consistent with each other

## Solution

Make the bindings of fields gated by `pbr_specular_textures` feature
consistent with the other gated bindings
2025-03-31 18:49:12 +00:00
HugoPeters1024
daba7a3c36
0.16 Regression fix: re-expose the display handle via a wrapper resource (#18644)
# Objective

- In the latest released version (15.3) I am able to obtain this
information by getting the actual `EventLoop` via `non_send_resource`.
Now that this object has (probably rightfully so) been replaced by the
`EventLoopProxy`, I can no longer maintain my custom render backend:
https://github.com/HugoPeters1024/bevy_vulkan. I also need the display
handle for a custom winit integration, for which I've made patches to
bevy before: XREF: https://github.com/bevyengine/bevy/pull/15884


## Solution

- Luckily, all that is required is exposing the `OwnedDisplayHandle` in
its own wrapper resource.

## Testing

- Aforementioned custom rendering backend works on this commit.

---------

Co-authored-by: HugoPeters1024 <hugopeters1024@gmail.com>
2025-03-31 18:47:08 +00:00
andriyDev
ec70a0f4f5
Delete unused weak handle and remove duplicate loads. (#18635)
# Objective

- Cleanup

## Solution

- Remove completely unused weak_handle
(`MESH_PREPROCESS_TYPES_SHADER_HANDLE`). This value is not used
directly, and is never populated.
- Delete multiple loads of `BUILD_INDIRECT_PARAMS_SHADER_HANDLE`. We
load it three times right after one another. This looks to be a
copy-paste error.

## Testing

- None.
2025-03-31 18:36:22 +00:00
Aevyrie
d09f958056
Parallelize bevy 0.16-rc bottlenecks (#18632)
# Objective

- Found stuttering and performance degradation while updating big_space
stress tests.

## Solution

- Identify and fix slow spots using tracy. Patch to verify fixes.

## Testing

- Tracy
- Before: 

![image](https://github.com/user-attachments/assets/ab7f440d-88c1-4ad9-9ad9-dca127c9421f)
- prev_gt parallelization and mutating instead of component insertion: 

![image](https://github.com/user-attachments/assets/9279a663-c0ba-4529-b709-d0f81f2a1d8b)
- parallelize visibility ranges and mesh specialization

![image](https://github.com/user-attachments/assets/25b70e7c-5d30-48ab-9bb2-79211d4d672f)

---------

Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2025-03-31 18:32:45 +00:00
Chris Russell
b4614dadcd
Use Display instead of Debug in the default error handler (#18629)
# Objective

Improve error messages for missing resources.  

The default error handler currently prints the `Debug` representation of
the error type instead of `Display`. Most error types use
`#[derive(Debug)]`, resulting in a dump of the structure, but will have
a user-friendly message for `Display`.

Follow-up to #18593

## Solution

Change the default error handler to use `Display` instead of `Debug`.  

Change `BevyError` to include the backtrace in the `Display` format in
addition to `Debug` so that it is still included.

## Showcase

Before: 

```
Encountered an error in system `system_name`: SystemParamValidationError { skipped: false, message: "Resource does not exist", param: "bevy_ecs::change_detection::Res<app_name::ResourceType>" }

Encountered an error in system `other_system_name`: "String message with\nmultiple lines."
```

After

```
Encountered an error in system `system_name`: Parameter `Res<ResourceType>` failed validation: Resource does not exist

Encountered an error in system `other_system_name`: String message with
multiple lines.
```
2025-03-31 18:28:19 +00:00
Robin KAY
6734abe3f5
Expose symbols needed to replicate SetMeshBindGroup in ecosystem crates. (#18613)
# Objective

My ecosystem crate, bevy_mod_outline, currently uses `SetMeshBindGroup`
as part of its custom rendering pipeline. I would like to allow for
possibility that, due to changes in 0.16, I need to customise the
behaviour of `SetMeshBindGroup` in order to make it work. However, not
all of the symbol needed to implement this render command are public
outside of Bevy.

## Solution

- Include `MorphIndices` in re-export list. I feel this is morally
equivalent to `SkinUniforms` already being exported.
- Change `MorphIndex::index` field to be public. I feel this is morally
equivalent to the `SkinByteOffset::byte_offset` field already being
public.
- Change `RenderMeshIntances::mesh_asset_id()` to be public (although
since all the fields of `RenderMeshInstances` are public it's possible
to work around this one by reimplementing).

These changes exclude:
- Making any change to the `RenderLightmaps` type as I don't need to
bind the light-maps for my use-case and I wanted to keep these changes
minimal. It has a private field which would need to be public or have
access methods.
- The changes already included in #18612.

## Testing

Confirmed that a copy of `SetMeshBindGroup` can be compiled outside of
Bevy with these changes, provided that the light-map code is removed.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-03-31 18:27:40 +00:00
charlotte
592822b702
Remove entities from specialization caches when despawned. (#18627)
# Objective

Fixes #17872 

## Solution

This should have basically no impact on static scenes. We can optimize
more later if anything comes up. Needing to iterate the two level bin is
a bit unfortunate but shouldn't matter for apps that use a single
camera.
2025-03-31 18:15:11 +00:00
BD103
bb87bd4d02
Improve Query's top-level documentation (#18622)
# Objective

- There's been several changes to `Query` for this release cycle, and
`Query`'s top-level documentation has gotten slightly out-of-date.
- Alternative to #18615.

## Solution

- Edit `Query`'s docs for consistency, clarity, and correctness.
- Make sure to group `get()` and `get_many()` together instead of
`single()` and `get_many()`, to enforce the distinction from
https://github.com/bevyengine/bevy/pull/18615#issuecomment-2764355672.
- Reformat doc tests so they would be readable if extracted into their
own file. (Which mainly involves adding more spacing.)
- Move link definitions to be nearer where they are used.
- Fix the tables so they are up-to-date and correctly escape square
brackets `\[ \]`.

## Testing

I ran `cargo doc -p bevy_ecs --no-deps` to view the docs and `cargo test
-p bevy_ecs --doc` to test the doc comments.

## Reviewing

The diff is difficult to read, so I don't recommend _just_ looking at
that. Instead, run `cargo doc -p bevy_ecs --no-deps` locally and read
through the new version. It should theoretically read smoother with less
super-technical jargon. :)

## Follow-up

I want to go through some of `Query`'s methods, such as `single()`,
`get()`, and `get_many()`, but I'll leave that for another PR.

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-03-31 18:12:24 +00:00
charlotte
9de7bd4d87
Use GpuPreprocessingMode::None if features not supported. (#18630)
# Objective

Fixes #18463 

## Solution

The features didn't seem to be getting checked for selecting
`GpuPreprocessingMode::None`.
2025-03-31 18:10:22 +00:00
charlotte
17e3efac12
Fix mesh extraction for meshes without associated material. (#18631)
# Objective

Fixes #17986
Fixes #18608

## Solution

Guard against situations where an extracted mesh does not have an
associated material. The way that mesh is dependent on the material api
(although decoupled) here is a bit unfortunate and we might consider
ways in the future to support these material features without this
indirect dependency.
2025-03-31 18:09:27 +00:00
charlotte
95b9117eac
Add required shader defs for environment map binding arrays in deferred (#18634)
# Objective

Fixes #18468

## Solution

Missing shader defs caused shader compilation failure.
2025-03-31 18:07:54 +00:00
charlotte
e98fe0a01c
Fix mesh tag feature for 2d. (#18636)
# Objective

Fixes #18564
2025-03-31 18:07:20 +00:00
charlotte
a895e87a28
Fix AsBindGroup hygenic issues with storage texture. (#18640)
# Objective

Fixes #18573
2025-03-31 18:06:33 +00:00
charlotte
c44dd39bdd
Only send unused event when final handle is dropped. (#18641)
# Objective

Fixes #18457

## Solution

Move the Unused even after the check for existing strong handles.
2025-03-31 18:05:59 +00:00
Waridley
89e00b19c4
Add compute_*_normals benchmarks (#18648)
# Objective

Benchmark current `compute_*_normals` methods to get a baseline as
requested in
https://github.com/bevyengine/bevy/pull/18552#issuecomment-2764875143

## Solution

Since the change to the default smooth normals method will definitely
cause a regression, but the previous method will remain as an option, I
added two technically-redundant benchmarks but with different names:
`smooth_normals` for whatever default weighting method is used, and
`face_weighted_normals` to benchmark the area-weighted method regardless
of what the default is. Then I'm adding `angle_weighted_normals` in
#18552. I also added `flat_normals` for completeness.
2025-03-31 17:58:45 +00:00
François Mockers
301f61845a
remove bevy_log as a dev-dependency from bevy_asset (#18619)
# Objective

- the bevy workspace fails to publish
```
   Packaging bevy_asset v0.16.0-dev (/home/runner/work/bevy-releasability/bevy-releasability/crates/bevy_asset)
    Updating crates.io index
    Updating `kellnr` index
error: failed to prepare local package for uploading

Caused by:
  no matching package named `bevy_log` found
  location searched: `kellnr` index
  required by package `bevy_asset v0.16.0-dev (/home/runner/work/bevy-releasability/bevy-releasability/crates/bevy_asset)`
```
-
https://github.com/TheBevyFlock/bevy-releasability/actions/runs/14153238476/job/39649160443

## Solution

- Remove bevy_log dev-dependency from bevy_asset
- Not sure of why this is a problem, but the dev-dependency is not
really needed so... 🤷
2025-03-30 16:03:01 +00:00
Vic
35cfef7cf2
Rename EntityBorrow/TrustedEntityBorrow to ContainsEntity/EntityEquivalent (#18470)
# Objective

Fixes #9367.

Yet another follow-up to #16547.

These traits were initially based on `Borrow<Entity>` because that trait
was what they were replacing, and it felt close enough in meaning.
However, they ultimately don't quite match: `borrow` always returns
references, whereas `EntityBorrow` always returns a plain `Entity`.
Additionally, `EntityBorrow` can imply that we are borrowing an `Entity`
from the ECS, which is not what it does.

Due to its safety contract, `TrustedEntityBorrow` is important an
important and widely used trait for `EntitySet` functionality.
In contrast, the safe `EntityBorrow` does not see much use, because even
outside of `EntitySet`-related functionality, it is a better idea to
accept `TrustedEntityBorrow` over `EntityBorrow`.

Furthermore, as #9367 points out, abstracting over returning `Entity`
from pointers/structs that contain it can skip some ergonomic friction.

On top of that, there are aspects of #18319 and #18408 that are relevant
to naming:
We've run into the issue that relying on a type default can switch
generic order. This is livable in some contexts, but unacceptable in
others.

To remedy that, we'd need to switch to a type alias approach: 
The "defaulted" `Entity` case becomes a
`UniqueEntity*`/`Entity*Map`/`Entity*Set` alias, and the base type
receives a more general name. `TrustedEntityBorrow` does not mesh
clearly with sensible base type names.

## Solution
Replace any `EntityBorrow` bounds with `TrustedEntityBorrow`.
+
Rename them as such:
`EntityBorrow` -> `ContainsEntity`
`TrustedEntityBorrow` -> `EntityEquivalent`

For `EntityBorrow` we produce a change in meaning; We designate it for
types that aren't necessarily strict wrappers around `Entity` or some
pointer to `Entity`, but rather any of the myriad of types that contain
a single associated `Entity`.
This pattern can already be seen in the common `entity`/`id` methods
across the engine.
We do not mean for `ContainsEntity` to be a trait that abstracts input
API (like how `AsRef<T>` is often used, f.e.), because eliding
`entity()` would be too implicit in the general case.

We prefix "Contains" to match the intuition of a struct with an `Entity`
field, like some contain a `length` or `capacity`.
It gives the impression of structure, which avoids the implication of a
relationship to the `ECS`.
`HasEntity` f.e. could be interpreted as "a currently live entity", 

As an input trait for APIs like #9367 envisioned, `TrustedEntityBorrow`
is a better fit, because it *does* restrict itself to strict wrappers
and pointers. Which is why we replace any
`EntityBorrow`/`ContainsEntity` bounds with
`TrustedEntityBorrow`/`EntityEquivalent`.

Here, the name `EntityEquivalent` is a lot closer to its actual meaning,
which is "A type that is both equivalent to an `Entity`, and forms the
same total order when compared".
Prior art for this is the
[`Equivalent`](https://docs.rs/hashbrown/latest/hashbrown/trait.Equivalent.html)
trait in `hashbrown`, which utilizes both `Borrow` and `Eq` for its one
blanket impl!

Given that we lose the `Borrow` moniker, and `Equivalent` can carry
various meanings, we expand on the safety comment of `EntityEquivalent`
somewhat. That should help prevent the confusion we saw in
[#18408](https://github.com/bevyengine/bevy/pull/18408#issuecomment-2742094176).

The new name meshes a lot better with the type aliasing approach in
#18408, by aligning with the base name `EntityEquivalentHashMap`.
For a consistent scheme among all set types, we can use this scheme for
the `UniqueEntity*` wrapper types as well!
This allows us to undo the switched generic order that was introduced to
`UniqueEntityArray` by its `Entity` default.

Even without the type aliases, I think these renames are worth doing!

## Migration Guide

Any use of `EntityBorrow` becomes `ContainsEntity`.
Any use of `TrustedEntityBorrow` becomes `EntityEquivalent`.
2025-03-30 06:04:26 +00:00
Vic
f57c7a43c4
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.
2025-03-30 03:51:14 +00:00
François Mockers
b0c4467398
bevy_image: derive TypePath when Reflect is not available (#18501)
# Objective

- bevy_image fails to build without default features:
```
error[E0277]: `image::Image` does not implement `TypePath` so cannot provide static type path information
   --> crates/bevy_image/src/image.rs:341:12
    |
341 | pub struct Image {
    |            ^^^^^ the trait `bevy_reflect::type_path::TypePath` is not implemented for `image::Image`
    |
    = note: consider annotating `image::Image` with `#[derive(Reflect)]` or `#[derive(TypePath)]`
    = help: the following other types implement trait `bevy_reflect::type_path::TypePath`:
              &'static Location<'static>
              &'static T
              &'static mut T
              ()
              (P,)
              (P1, P0)
              (P1, P2, P0)
              (P1, P2, P3, P0)
            and 146 others
note: required by a bound in `Asset`
   --> /home/runner/work/bevy-releasability/bevy-releasability/crates/bevy_asset/src/lib.rs:415:43
    |
415 | pub trait Asset: VisitAssetDependencies + TypePath + Send + Sync + 'static {}
    |                                           ^^^^^^^^ required by this bound in `Asset`
    = note: `Asset` is a "sealed trait", because to implement it you also need to implement `bevy_reflect::type_path::TypePath`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it
    = help: the following types implement the trait:
              bevy_asset::AssetIndex
              bevy_asset::LoadedUntypedAsset
              bevy_asset::AssetEvent<A>
              bevy_asset::LoadedFolder
              bevy_asset::StrongHandle
              bevy_asset::Handle<A>
              bevy_asset::AssetId<A>
              bevy_asset::AssetPath<'a>
            and 148 others

error[E0277]: `image::Image` does not implement `TypePath` so cannot provide static type path information
   --> crates/bevy_image/src/image_loader.rs:121:18
    |
121 |     type Asset = Image;
    |                  ^^^^^ the trait `bevy_reflect::type_path::TypePath` is not implemented for `image::Image`
    |
    = note: consider annotating `image::Image` with `#[derive(Reflect)]` or `#[derive(TypePath)]`
    = help: the following other types implement trait `bevy_reflect::type_path::TypePath`:
              &'static Location<'static>
              &'static T
              &'static mut T
              ()
              (P,)
              (P1, P0)
              (P1, P2, P0)
              (P1, P2, P3, P0)
            and 146 others
    = note: required for `<ImageLoader as AssetLoader>::Asset` to implement `Asset`
note: required by a bound in `bevy_asset::AssetLoader::Asset`
   --> /home/runner/work/bevy-releasability/bevy-releasability/crates/bevy_asset/src/loader.rs:33:17
    |
33  |     type Asset: Asset;
    |                 ^^^^^ required by this bound in `AssetLoader::Asset`

error[E0277]: `texture_atlas::TextureAtlasLayout` does not implement `TypePath` so cannot provide static type path information
   --> crates/bevy_image/src/texture_atlas.rs💯12
    |
100 | pub struct TextureAtlasLayout {
    |            ^^^^^^^^^^^^^^^^^^ the trait `bevy_reflect::type_path::TypePath` is not implemented for `texture_atlas::TextureAtlasLayout`
    |
    = note: consider annotating `texture_atlas::TextureAtlasLayout` with `#[derive(Reflect)]` or `#[derive(TypePath)]`
    = help: the following other types implement trait `bevy_reflect::type_path::TypePath`:
              &'static Location<'static>
              &'static T
              &'static mut T
              ()
              (P,)
              (P1, P0)
              (P1, P2, P0)
              (P1, P2, P3, P0)
            and 146 others
note: required by a bound in `Asset`
   --> /home/runner/work/bevy-releasability/bevy-releasability/crates/bevy_asset/src/lib.rs:415:43
    |
415 | pub trait Asset: VisitAssetDependencies + TypePath + Send + Sync + 'static {}
    |                                           ^^^^^^^^ required by this bound in `Asset`
    = note: `Asset` is a "sealed trait", because to implement it you also need to implement `bevy_reflect::type_path::TypePath`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it
    = help: the following types implement the trait:
              bevy_asset::AssetIndex
              bevy_asset::LoadedUntypedAsset
              bevy_asset::AssetEvent<A>
              bevy_asset::LoadedFolder
              bevy_asset::StrongHandle
              bevy_asset::Handle<A>
              bevy_asset::AssetId<A>
              bevy_asset::AssetPath<'a>
            and 148 others
```
- `Asset` trait depends on `TypePath` which is in bevy_reflect. it's
usually implemented by the `Reflect` derive


## Solution

- make bevy_reflect not an optional dependency
- when feature `bevy_reflect` is not enabled, derive `TypePath` directly
2025-03-30 02:50:24 +00:00
Aevyrie
8130b229be
Transform Propagation Optimization: Static Subtree Marking (#18589)
# Objective

- Optimize static scene performance by marking unchanged subtrees.
-
[bef0209](bef0209de1)
fixes #18255 and #18363.
- Closes #18365 
- Includes change from #18321

## Solution

- Mark hierarchy subtrees with dirty bits to avoid transform propagation
where not needed
- This causes a performance regression when spawning many entities, or
when the scene is entirely dynamic.
- This results in massive speedups for largely static scenes.
- In the future we could allow the user to change this behavior, or add
some threshold based on how dynamic the scene is?

## Testing

- Caldera Hotel scene
2025-03-30 02:43:39 +00:00
JMS55
4519ff677a
Fix diffuse transmission for anisotropic materials (#18610)
Expand the diff, this was obviously just a copy paste bug at some point.
2025-03-30 02:39:10 +00:00
Chris Russell
30ee5ffe3b
Improve error message for missing resources (#18593)
# Objective

Fixes #18515 

After the recent changes to system param validation, the panic message
for a missing resource is currently:

```
Encountered an error in system `missing_resource_error::res_system`: SystemParamValidationError { skipped: false }
```

Add the parameter type name and a descriptive message, improving the
panic message to:

```
Encountered an error in system `missing_resource_error::res_system`: SystemParamValidationError { skipped: false, message: "Resource does not exist", param: "bevy_ecs::change_detection::Res<missing_resource_error::MissingResource>" }
```

## Solution

Add fields to `SystemParamValidationError` for error context. Include
the `type_name` of the param and a message.

Store them as `Cow<'static, str>` and only format them into a friendly
string in the `Display` impl. This lets us create errors using a
`&'static str` with no allocation or formatting, while still supporting
runtime `String` values if necessary.

Add a unit test that verifies the panic message.

## Future Work

If we change the default error handling to use `Display` instead of
`Debug`, and to use `ShortName` for the system name, the panic message
could be further improved to:

```
Encountered an error in system `res_system`: Parameter `Res<MissingResource>` failed validation: Resource does not exist
```

However, `BevyError` currently includes the backtrace in `Debug` but not
`Display`, and I didn't want to try to change that in this PR.
2025-03-30 02:38:17 +00:00
Brian Reavis
8ece2ee07b
Fix NonMesh draw command item queries (#17893)
# Objective

This fixes `NonMesh` draw commands not receiving render-world entities
since
- https://github.com/bevyengine/bevy/pull/17698

This unbreaks item queries for queued non-mesh entities:

```rust
struct MyDrawCommand {
    type ItemQuery = Read<DynamicUniformIndex<SomeUniform>>;
    // ...
}
```

### Solution

Pass render entity to `NonMesh` draw commands instead of
`Entity::PLACEHOLDER`. This PR also introduces sorting of the `NonMesh`
bin keys like other types, which I assume is the intended behavior.
@pcwalton

## Testing

- Tested on a local project that extensively uses `NonMesh` items.
2025-03-30 02:33:09 +00:00
Kristoffer Søholm
9214fd649d
Fix compilation of compile_fail_utils when not using rustup (#18394)
# Objective

Currently the `compile_fail_utils` crate fails to compile (ironic) when
the `RUSTUP_HOME` env var isn't set. This has been the case for a long
time, but I only noticed it recently due to rust-analyzer starting to
show the error.

## Solution

Only filter the logs for the `RUSTUP_HOME` variable if it's set.
2025-03-30 02:33:03 +00:00
Ian Hobson
9e7153ecdb
Update the version of glam required by bevy_reflect to 0.29.2 (#18592)
# Objective

- Avoid breaking builds for projects that have glam `0.29.0` in their
`Cargo.lock` files.

## Solution

Reflection support for additional `glam` types were added in #17493,
which were only introduced to `glam` in [`0.29.1`][glam-changelog]. If
you have a `Cargo.lock` file that refers to `0.29.0`, then `bevy_derive`
will fail to compile.

The workaround is easy enough once you figure out what's going on, but
specifying the required minimum will avoid the paper cut for others.

`0.29.2` is used here as the required version to include the fix for a
regression that was introduced `0.29.1`.

[glam-changelog]:
<https://github.com/bitshifter/glam-rs/blob/main/CHANGELOG.md#0291---2024-10-30>
2025-03-30 02:13:53 +00:00
Robin KAY
041d3d7c92
Expose skins_use_uniform_buffers() necessary to use pre-existing setup_morph_and_skinning_defs() API. (#18612)
# Objective

As of bevy 0.16-dev, the pre-existing public function
`bevy::pbr::setup_morph_and_skinning_defs()` is now passed a boolean
flag called `skins_use_uniform_buffers`. The value of this boolean is
computed by the function
`bevy_pbr::render::skin::skins_use_uniform_buffers()`, but it is not
exported publicly.

Found while porting
[bevy_mod_outline](https://github.com/komadori/bevy_mod_outline) to
0.16.

## Solution

Add `skin::skins_use_uniform_buffers` to the re-export list of
`bevy_pbr::render`.

## Testing

Confirmed test program can access public API.
2025-03-30 01:31:10 +00:00
aloucks
d1f3d950a1
Fix shader pre-pass compile failure when using AlphaMode::Blend and a Mesh without UVs (0.16.0-rc.2) (#18602)
# Objective

The flags are referenced later outside of the VERTEX_UVS ifdef/endif
block. The current behavior causes the pre-pass shader to fail to
compile when UVs are not present in the mesh, such as when using a
`LineStrip` to render a grid.

Fixes #18600

## Solution

Move the definition of the `flags` outside of the ifdef/endif block.

## Testing

Ran a modified `3d_example` that used a mesh and material with
alpha_mode blend, `LineStrip` topology, and no UVs.
2025-03-30 01:25:42 +00:00
Gino Valente
f4716034fa
bevy_reflect: Fix TypePath string concatenation (#18609)
# Objective

Fixes #18606

When a type implements `Add` for `String`, the compiler can get confused
when attempting to add a `&String` to a `String`.

Unfortunately, this seems to be [expected
behavior](https://github.com/rust-lang/rust/issues/77143#issuecomment-698369286)
which causes problems for generic types since the current `TypePath`
derive generates code that appends strings in this manner.

## Solution

Explicitly use the `Add<&str>` implementation in the `TypePath` derive
macro.

## Testing

You can test locally by running:

```
cargo check -p bevy_reflect --tests
```
2025-03-29 21:01:53 +00:00
Satellile
58b8f554a1
Fix LogDiagnosticsPlugin log target typo (#18534)
# Objective

For the LogDiagnosticsPlugin, the log target is "bevy diagnostic" with a
space; I think it may (?) be a typo intended to be "bevy_diagnostic"
with an underline.

I couldn't get filtering INFO level logs with work with this plugin,
changing this seems to produce the expected behavior.
2025-03-29 17:07:21 +00:00
François Mockers
3945a6de3b
Fix wesl in wasm and webgl2 (#18591)
# Objective

- feature `shader_format_wesl` doesn't compile in Wasm
- once fixed, example `shader_material_wesl` doesn't work in WebGL2

## Solution

- remove special path handling when loading shaders. this seems like a
way to escape the asset folder which we don't want to allow, and can't
compile on android or wasm, and can't work on iOS (filesystem is rooted
there)
- pad material so that it's 16 bits. I couldn't get conditional
compilation to work in wesl for type declaration, it fails to parse
- the shader renders the color `(0.0, 0.0, 0.0, 0.0)` when it's not a
polka dot. this renders as black on WebGPU/metal/..., and white on
WebGL2. change it to `(0.0, 0.0, 0.0, 1.0)` so that it's black
everywhere
2025-03-28 21:45:02 +00:00
Alice Cecile
4a31d8e91a
Use desired language in migration guide template (#18576)
# Objective

Both reading and writing migration guides is easier when the language is
standardized.

However, migration guide authors don't have clear guidelines for the
tone and phrasing to use.

## Solution

Communicate this information to authors by creating stub text with a
clear and polite standard style.

We could instead write a style guide, but turning style guides into a
writing style is slower and much harder than simply filling in the
blanks. While style guides are a good fit for more free-form writing,
they don't work well for the very mechanical and dry migration guides.

---------

Co-authored-by: Miles Silberling-Cook <NthTensor@users.noreply.github.com>
2025-03-28 16:11:12 +00:00
JMS55
b3c83465b3
Fix and improve tracy rendering spans (#18588)
* `submit_graph_commands` was incorrectly timing the command buffer
generation tasks as well, and not only the queue submission. Moved the
span to fix that.
* Added a new `command_buffer_generation_tasks` span as a parent for all
the individual command buffer generation tasks that don't run as part of
the Core3d span.


![image](https://github.com/user-attachments/assets/5a20c2f5-f1df-4c03-afbb-4865327aea33)
2025-03-28 05:35:47 +00:00
JMS55
f4a5e8bc51
Tracy GPU support (#18490)
# Objective

- Add tracy GPU support

## Solution

- Build on top of the existing render diagnostics recording to also
upload gpu timestamps to tracy
- Copy code from https://github.com/Wumpf/wgpu-profiler

## Showcase

![image](https://github.com/user-attachments/assets/4dd7a7cd-bc0b-43c3-8390-6783dfda6473)
2025-03-28 04:57:01 +00:00
Nick
d20f8553bf
Fix misleading documentation of Main schedule (#18579)
# Objective

Fixes #18562.

## Solution

- Specified that `StateTransition` is actually run before `PreStartup`.
- Specified consequences of this and how to actually run systems before
any game logic regardless of state.
- Updated docs of `StateTransition` to reflect that it is run before
`PreStartup` in addition to being run after `PreUpdate`.

## Testing

- `cargo doc`
- `cargo test --doc`
2025-03-28 01:12:19 +00:00
Al M.
e50a42ad8b
Fix various unused import warnings with no features enabled (#18580)
# Objective

Per title. I was using the `bevy_gizmos` crate without the `webgl`
feature enabled, and noticed there were other warnings with no features
enabled as well.

## Testing

- `cargo check -p bevy_gizmos --no-default-features`
- `cargo check -p bevy_gizmos --all-features`
- `cargo run -p ci -- test`
- Ran gizmo examples.
2025-03-28 00:21:16 +00:00
Guillaume Gomez
0afd9ea806
Update sysinfo version to 0.34.0 (#18581)
Lot of improvements and stuff. You can see the full list
[here](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md).
:)
2025-03-28 00:00:27 +00:00
JMS55
000b9e52c9
Have the mesh allocator handle modified meshes (#18531)
# Objective
Fixes https://github.com/bevyengine/bevy/issues/16586.

## Solution
- Free meshes before allocating new ones (so hopefully the existing
allocation is used, but it's not guaranteed since it might end up
getting used by a smaller mesh first).
- Keep track of modified render assets, and have the mesh allocator free
their allocations.
- Cleaned up some render asset code to make it more understandable,
since it took me several minutes to reverse engineer/remember how it was
supposed to work.

Long term we'll probably want to explicitly reusing allocations for
modified meshes that haven't grown in size, or do delta uploads using a
compute shader or something, but this is an easy fix for the near term.

## Testing
Ran the example provided in the issue. No crash after a few minutes, and
memory usage remains steady.
2025-03-27 21:39:09 +00:00
krunchington
83ffc90c6c
Fix relationship macro for multiple named members fields (#18530)
# Objective

Fixes #18466 

## Solution

Updated the macro generation pattern to place the comma in the correct
place in the pattern.

## Testing

- Tried named and unnamed fields in combination, and used rust expand
macro tooling to see the generated code and verify its correctness (see
screenshots in example below)

---

## Showcase

Screenshot showing expanded macro with multiple named fields

![image](https://github.com/user-attachments/assets/7ecd324c-10ba-4b23-9b53-b94da03567d3)

Screenshot showing expanded macro with single unnamed field

![image](https://github.com/user-attachments/assets/be72f061-5f07-4d19-b5f6-7ff6c35ec679)

## Migration Guide

n/a
2025-03-27 21:35:47 +00:00
Greeble
a7e6578733
Fix animation transitions affecting other entities (#18572)
## Objective

 Fix #18557.

## Solution

As described in the bug, `remaining_weight` should have been inside the
loop.

## Testing

Locally changed the `animated_mesh_control` example to spawn multiple
meshes and play different transitions.
2025-03-27 21:33:25 +00:00
andriyDev
a08760b19b
Revert PR #15481 to resolve a regression. (#18567)
# Objective

- Fixes #18010.

## Solution

- Revert the offending PRs! These are #15481 and #18013. We now no
longer get an error if there are duplicate subassets.
- In theory we could untangle #18013 from #15481, but that may be
tricky, and may still introduce regressions. To avoid this worry (since
we're already in RC mode), I am just reverting both.

## Testing

- This is just a revert.

---

## Migration Guide

<Remove the migration guides for #15481 and #18013>

I will make a PR to the bevy_website repo after this is merged.
2025-03-27 21:32:01 +00:00
Alice Cecile
00a84d657a
Delete migration guide section from PR template (#18575)
# Objective

Due to the work outlined in #18441, we're no longer storing the
migration guides on the PR description.

## Solution

Delete the section of the PR template that suggests you do this.
2025-03-27 21:20:33 +00:00
Carter Anderson
1ba9da0812
Required Components: pass through all tokens in {} and () syntax (#18578)
# Objective

#18555 added improved require syntax, but inline structs didn't support
`..Default::default()` syntax (for technical reasons we can't parse the
struct directly, so there is manual logic that missed this case).

## Solution

When a `{}` or `()` section is encountered for a required component,
rather than trying to parse the fields directly, just pass _all_ of the
tokens through. This ensures no tokens are dropped, protects us against
any future syntax changes, and optimizes our parsing logic (as we're
dropping the field parsing logic entirely).
2025-03-27 21:20:08 +00:00
Miles Silberling-Cook
0a2e183476
Add basic release content tagging workflow (#18568)
# Objective

This PR begins integrating the new releate-content drafting process
(https://github.com/bevyengine/bevy/pull/18427) into our GitHub
workflows. It's similar to what we had before: Messages are posted to
PRs tagged with `M-Needs-Release-Note` or `M-Needs-Migration-Guide`
asking them to add the required material and linking to the
instructions. These messages do not trigger if the PR already has
modified files in the `release-notes` or `migration-guides` directories
(respectively).

I have also re-arranged and content slightly (to remove the need for a
directory with the current version number), tweaked the language, and
switched the templates to use the [standard markdown frontmatter
format](https://jekyllrb.com/docs/front-matter/).

## Reviewer Questions

+ Do we want to add a CI rule actually requiring tagged PRs to
create/modify files in the correct directories, or is the message prompt
enough?
+ Do we want to add a CI rule to lint the metadata, for example to
enforce that the PR number is included in the files it modifies?
2025-03-27 17:46:39 +00:00
François Mockers
0d90da896b
don't wait during publishing (#18563)
# Objective

- Publishing takes a long time
- There's a 20 second wait between crates to not hit the rate limit on
crates.io

## Solution

- Our rate limit has been increased by the crates.io team, don't wait
anymore!
2025-03-26 21:27:29 +00:00
Carter Anderson
538afe2330
Improved Require Syntax (#18555)
# Objective

Requires are currently more verbose than they need to be. People would
like to define inline component values. Additionally, the current
`#[require(Foo(custom_constructor))]` and `#[require(Foo(|| Foo(10))]`
syntax doesn't really make sense within the context of the Rust type
system. #18309 was an attempt to improve ergonomics for some cases, but
it came at the cost of even more weirdness / unintuitive behavior. Our
approach as a whole needs a rethink.

## Solution

Rework the `#[require()]` syntax to make more sense. This is a breaking
change, but I think it will make the system easier to learn, while also
improving ergonomics substantially:

```rust
#[derive(Component)]
#[require(
    A, // this will use A::default()
    B(1), // inline tuple-struct value
    C { value: 1 }, // inline named-struct value
    D::Variant, // inline enum variant
    E::SOME_CONST, // inline associated const
    F::new(1), // inline constructor
    G = returns_g(), // an expression that returns G
    H = SomethingElse::new(), // expression returns SomethingElse, where SomethingElse: Into<H> 
)]
struct Foo;
```

## Migration Guide

Custom-constructor requires should use the new expression-style syntax:

```rust
// before
#[derive(Component)]
#[require(A(returns_a))]
struct Foo;

// after
#[derive(Component)]
#[require(A = returns_a())]
struct Foo;
```

Inline-closure-constructor requires should use the inline value syntax
where possible:

```rust
// before
#[derive(Component)]
#[require(A(|| A(10))]
struct Foo;

// after
#[derive(Component)]
#[require(A(10)]
struct Foo;
```

In cases where that is not possible, use the expression-style syntax:

```rust
// before
#[derive(Component)]
#[require(A(|| A(10))]
struct Foo;

// after
#[derive(Component)]
#[require(A = A(10)]
struct Foo;
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
2025-03-26 17:48:27 +00:00
Greeble
10f1fbf589
Remove unused variable AnimationPlayer::blend_weights. (#18560)
This variable was cruelly abandoned in #15589.

Seems fairly safe to remove as it's private. I'm assuming something
could have used it via reflection, but that seems unlikely

## Testing

```
cargo run --example animated_mesh
cargo run --example animation_graph
```
2025-03-26 17:42:54 +00:00
IceSentry
f0503930df
Use current_exe for default window title (#18553)
# Objective

- We currently default to "App" for the window title, it would be nice
if examples had more descriptive names

## Solution

- Use `std::env::current_exe` to try to figure out a default title. If
it's not present. Use "App".

## Testing

- I tested that examples that set a custom title still use the custom
title and that examples without a custom title use the example name

---

### Showcase

Here's the 3d_scene example:


![image](https://github.com/user-attachments/assets/bc67edc7-4211-4479-a027-ee6c52b0bd02)

### Notes

Here's a previous attempt at this from a few years ago
https://github.com/bevyengine/bevy/pull/3404

There's some relevant discussion in there, but cart's decision was to
default to "App" when no name was found.

---------

Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2025-03-26 17:32:18 +00:00
Greeble
a02cdaa017
Update AnimatableProperty documentation, reduce crate dependencies (#18543)
## Objective

- Remove the second to last `bevy_animation` dependency on
`bevy_render`.
- Update some older documentation to reflect later changes to the crate.

## Narrative

I'm trying to make `bevy_animation` independent of `bevy_render`. The
documentation for `bevy_animation::AnimatableProperty` is one of the
last few dependencies. It uses `bevy_render::Projection` to demonstrate
animating an arbitrary value, but I thought that could be easily swapped
for something else.

I then realised that the rest of the documentation was a bit out of
date. Originally `AnimatableProperty` was the only way to animate a
property and so the documentation was quite detailed. But over time the
crate has gained more documentation and other ways to hook up
properties, leaving parts of the docs stale or covered elsewhere. So
I've slimmed down the `AnimatableProperty` docs and added a link to the
main alternative (`animated_field`).

I've probably swung too far towards brevity, so I can build them back up
if preferred. Also the example is kinda contrived and doesn't show the
range of `AnimatableProperty`, like being able to choose different
components. And finally the memes might be a bit stale?

## Showcase


![image](https://github.com/user-attachments/assets/23f1c0bf-10ea-4602-a566-673abe5dace7)

## Testing

```
cargo doc -p bevy_animation --no-deps --all-features
cargo test -p bevy_animation --doc --all-features
```
2025-03-26 13:43:32 +00:00
Chris Russell
5d1fe16bfd
Fix run_system for adapter systems wrapping exclusive systems (#18406)
# Objective

Fix panic in `run_system` when running an exclusive system wrapped in a
`PipeSystem` or `AdapterSystem`.

#18076 introduced a `System::run_without_applying_deferred` method. It
normally calls `System::run_unsafe`, but
`ExclusiveFunctionSystem::run_unsafe` panics, so it was overridden for
that type. Unfortunately, `PipeSystem::run_without_applying_deferred`
still calls `PipeSystem::run_unsafe`, which can then call
`ExclusiveFunctionSystem::run_unsafe` and panic.

## Solution

Make `ExclusiveFunctionSystem::run_unsafe` work instead of panicking.
Clarify the safety requirements that make this sound.

The alternative is to override `run_without_applying_deferred` in
`PipeSystem`, `CombinatorSystem`, `AdapterSystem`,
`InfallibleSystemWrapper`, and `InfallibleObserverWrapper`. That seems
like a lot of extra code just to preserve a confusing special case!

Remove some implementations of `System::run` that are no longer
necessary with this change. This slightly changes the behavior of
`PipeSystem` and `CombinatorSystem`: Currently `run` will call
`apply_deferred` on the first system before running the second, but
after this change it will only call it after *both* systems have run.
The new behavior is consistent with `run_unsafe` and
`run_without_applying_deferred`, and restores the behavior prior to
#11823.

The panic was originally necessary because [`run_unsafe` took
`&World`](https://github.com/bevyengine/bevy/pull/6083/files#diff-708dfc60ec5eef432b20a6f471357a7ea9bfb254dc2f918d5ed4a66deb0e85baR90).
Now that it takes `UnsafeWorldCell`, it is possible to make it work. See
also Cart's concerns at
https://github.com/bevyengine/bevy/pull/4166#discussion_r979140356,
although those also predate `UnsafeWorldCell`.

And see #6698 for a previous bug caused by this panic.
2025-03-26 13:40:42 +00:00
François Mockers
7a8dfdaec2
don't include file not available on docs.rs (#18551)
# Objective

- Fixes #18539 
- Doc failed to build as an example `include_str!` an asset, but assets
are not available in the packaged crate

## Solution

- Don't `include_str!` the shader but read it at runtime
2025-03-26 06:07:39 +00:00
Chris Russell
837991a5b5
Replace ValidationOutcome with Result (#18541)
# Objective

Make it easier to short-circuit system parameter validation.  

Simplify the API surface by combining `ValidationOutcome` with
`SystemParamValidationError`.

## Solution

Replace `ValidationOutcome` with `Result<(),
SystemParamValidationError>`. Move the docs from `ValidationOutcome` to
`SystemParamValidationError`.

Add a `skipped` field to `SystemParamValidationError` to distinguish the
`Skipped` and `Invalid` variants.

Use the `?` operator to short-circuit validation in tuples of system
params.
2025-03-26 03:36:16 +00:00
ickshonpe
f74abb1b89
Add sprite flipping to testbed_2d's sprite scene (#18537)
# Objective

Add sprite flipping to `testbed_2d`'s sprite scene

## Solution

Draw the sprite flipped in each axis and both axes.

Changed the sprite to the rectangular bevy banner with text and made the
images different colors.

## Testing
```
cargo run --example testbed_2d
```


![image](https://github.com/user-attachments/assets/dcfe687b-2f40-4417-bb20-6c892b425228)

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
2025-03-25 21:14:17 +00:00
Zachary Harrold
921ff6701f
Add methods to work with dynamic immutable components (#18532)
# Objective

- Fixes #16861

## Solution

- Added: 
  - `UnsafeEntityCell::get_mut_assume_mutable_by_id`
  - `EntityMut::get_mut_assume_mutable_by_id`
  - `EntityMut::get_mut_assume_mutable_by_id_unchecked`
  - `EntityWorldMut::into_mut_assume_mutable_by_id`
  - `EntityWorldMut::into_mut_assume_mutable`
  - `EntityWorldMut::get_mut_assume_mutable_by_id`
  - `EntityWorldMut::into_mut_assume_mutable_by_id`
  - `EntityWorldMut::modify_component_by_id`
  - `World::modify_component_by_id`
  - `DeferredWorld::modify_component_by_id`
- Added `fetch_mut_assume_mutable` to `DynamicComponentFetch` trait
(this is a breaking change)

## Testing

- CI

---

## Migration Guide

If you had previously implemented `DynamicComponentFetch` you must now
include a definition for `fetch_mut_assume_mutable`. In general this
will be identical to `fetch_mut` using the relevant alternatives for
actually getting a component.

---

## Notes

All of the added methods are minor variations on existing functions and
should therefore be of low risk for inclusion during the RC process.
2025-03-25 20:52:07 +00:00
aloucks
d6d9f63101
Fix UpdateMode::Reactive behavior on Windows (#18493)
# Objective

The fix in #17488 forced Windows to always behave as if it were in
`UpdateMode::Continuous`.

CC https://github.com/bevyengine/bevy/pull/17991

## Solution

Removed the unconditional `redraw_requested = true` and added a check
for `Reactive` in `about_to_wait`.

## Testing

- Verified that the `low_power` example worked as expected with all
`UpdateMode` options.
- Verified that animation continued in both `eased_motion ` and
`low_power` examples when in `Continuous` update mode while:
  - Resizing the Window
  - Moving the window via clicking and dragging the title bar
- Verified that `window_settings` example still worked as expected.
- Verified that `monitor_info` example still worked as expected.
2025-03-25 20:29:50 +00:00
Brian Reavis
9d25689f82
Remove Image::from_buffer name argument (only present in debug "dds" builds) (#18538)
# Objective

- Fixes https://github.com/bevyengine/bevy/issues/17891
- Cherry-picked from https://github.com/bevyengine/bevy/pull/18411

## Solution

The `name` argument could either be made permanent (by removing the
`#[cfg(...)]` condition) or eliminated entirely. I opted to remove it,
as debugging a specific DDS texture edge case in GLTF files doesn't seem
necessary, and there isn't any other foreseeable need to have it.

## Migration Guide

- `Image::from_buffer()` no longer has a `name` argument that's only
present in debug builds when the `"dds"` feature is enabled. If you
happen to pass a name, remove it.
2025-03-25 19:25:01 +00:00
Eagster
834260845a
Ensure spawning related entities in an OnAdd observer downstream of a World::spawn in a Command does not cause a crash (#18545)
# Objective

fixes #18452.

## Solution

Spawning used to flush commands only, but those commands can reserve
entities. Now, spawning flushes everything, including reserved entities.
I checked, and this was the only place where `flush_commands` is used
instead of `flush` by mistake.

## Testing

I simplified the MRE from #18452 into its own test, which fails on main,
but passes on this branch.
2025-03-25 19:19:53 +00:00
Sorseg
f9cf277634
Make RayMap map public (#18544)
Migration guide:
# Objective

Currently there seems to be no way to enable picking through
render-to-texture cameras

## Solution

This PR allows casting rays from the game code quite easily.

## Testing

- I've tested these in my game and it seems to work
- I haven't tested edge cases

--- 

## Showcase

<details>
  <summary>Click to view showcase</summary>

```rust

fn cast_rays_from_additional_camera(
    cameras: Query<(&GlobalTransform, &Camera, Entity), With<RenderToTextureCamera>>,
    mut rays: ResMut<RayMap>,
    pointers: Query<(&PointerId, &PointerLocation)>,
) {
    for (camera_global_transform, camera, camera_entity) in &cameras {
        for (pointer_id, pointer_loc) in &pointers {
            let Some(viewport_pos) = pointer_loc.location() else {
                continue;
            };
            // if camera result is transformed in any way, the reverse transformation
            // should be applied somewhere here
            let ray = camera
                .viewport_to_world(camera_global_transform, viewport_pos.position)
                .ok();
            if let Some(r) = ray {
                rays.map.insert(RayId::new(camera_entity, *pointer_id), r);
            }
        }
    }
}

```

</details>

## Migration Guide
The `bevy_picking::backend::ray::RayMap::map` method is removed as
redundant,
In systems using `Res<RayMap>` replace `ray_map.map()` with
`&ray_map.map`
2025-03-25 19:15:20 +00:00
IceSentry
65d9a7535a
Move non-generic parts of the PrepassPipeline to internal field (#18322)
# Objective

- The prepass pipeline has a generic bound on the specialize function
but 95% of it doesn't need it

## Solution

- Move most of the fields to an internal struct and use a separate
specialize function for those fields

## Testing

- Ran the 3d_scene and it worked like before

---

## Migration Guide

If you were using a field of the `PrepassPipeline`, most of them have
now been move to `PrepassPipeline::internal`.

## Notes

Here's the cargo bloat size comparison (from this tool
https://github.com/bevyengine/bevy/discussions/14864):

```
before:
    (
        "<bevy_pbr::prepass::PrepassPipeline<M> as bevy_render::render_resource::pipeline_specializer::SpecializedMeshPipeline>::specialize",
        25416,
        0.05582993,
    ),

after:
    (
        "<bevy_pbr::prepass::PrepassPipeline<M> as bevy_render::render_resource::pipeline_specializer::SpecializedMeshPipeline>::specialize",
        2496,
        0.005490916,
    ),
    (
        "bevy_pbr::prepass::PrepassPipelineInternal::specialize",
        11444,
        0.025175499,
    ),
```

The size for the specialize function that is generic is now much
smaller, so users won't need to recompile it for every material.
2025-03-25 18:47:31 +00:00
IQuick 143
db923d6319
Fix mesh_picking not working due to mixing vertex and triangle indices. (#18533)
# Objective

- #18495 

## Solution

- The code in the PR #18232 accidentally used a vertex index as a
triangle index, causing the wrong triangle to be used for normal
computation and if the triangle went out of bounds, it would skip the
ray-hit.
- Don't do that.

## Testing

- Run `cargo run --example mesh_picking`
2025-03-25 18:27:35 +00:00
François Mockers
70c68417f0
don't flip sprites twice (#18535)
# Objective

- After #17041, sprite flipping doesn't work

## Solution

- Sprite flipping is applied twice:

b6ccc2a2a0/crates/bevy_sprite/src/render/mod.rs (L766-L773)

b6ccc2a2a0/crates/bevy_sprite/src/render/mod.rs (L792-L799)
- Keep one
2025-03-25 18:20:13 +00:00
Zachary Harrold
b6ccc2a2a0
Fix bevy_math/transform/input Improper Inclusion (#18526)
# Objective

Enabling `serialize`, `critical-section`, or `async-executor` would
improperly enable `bevy_math`, `bevy_input`, and/or `bevy_transform`.
This was caused by those crates previously being required but are now
optional (gated behind `std` and/or `libm`).

## Solution

- Added `?` to features not intended to enable those crates

## Testing

- CI
2025-03-25 08:20:51 +00:00
HugoPeters1024
a58a8bde2b
bugfix(frustra of point lights were not recalculated when a camera changes) (#18519)
# Objective

- Fixes https://github.com/bevyengine/bevy/issues/11682

## Solution

- https://github.com/bevyengine/bevy/pull/4086 introduced an
optimization to not do redundant calculations, but did not take into
account changes to the resource `global_lights`. I believe that my patch
includes the optimization benefit but adds the required nuance to fix
said bug.

## Testing

The example originally given by
[@kirillsurkov](https://github.com/kirillsurkov) and then updated by me
to bevy 15.3 here:
https://github.com/bevyengine/bevy/issues/11682#issuecomment-2746287416
will not have shadows without this patch:

```rust
use bevy::prelude::*;

#[derive(Resource)]
struct State {
    x: f32,
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(Update, update)
        .insert_resource(State { x: -40.0 })
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    commands.spawn((
        Mesh3d(meshes.add(Circle::new(4.0))),
        MeshMaterial3d(materials.add(Color::WHITE)),
    ));
    commands.spawn((
        Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
        MeshMaterial3d(materials.add(Color::linear_rgb(0.0, 1.0, 0.0))),
    ));
    commands.spawn((
        PointLight {
            shadows_enabled: true,
            ..default()
        },
        Transform::from_xyz(4.0, 8.0, 4.0),
    ));
    commands.spawn(Camera3d::default());
}

fn update(mut state: ResMut<State>, mut camera: Query<&mut Transform, With<Camera3d>>) {
    let mut camera = camera.single_mut().unwrap();

    let t = Vec3::new(state.x, 0.0, 10.0);
    camera.translation = t;
    camera.look_at(t - Vec3::Z, Vec3::Y);

    state.x = 0.0;
}
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
2025-03-25 04:48:49 +00:00
Alice Cecile
6a981aaa6f
Define system param validation on a per-system parameter basis (#18504)
# Objective

When introduced, `Single` was intended to simply be silently skipped,
allowing for graceful and efficient handling of systems during invalid
game states (such as when the player is dead).

However, this also caused missing resources to *also* be silently
skipped, leading to confusing and very hard to debug failures. In
0.15.1, this behavior was reverted to a panic, making missing resources
easier to debug, but largely making `Single` (and `Populated`)
worthless, as they would panic during expected game states.

Ultimately, the consensus is that this behavior should differ on a
per-system-param basis. However, there was no sensible way to *do* that
before this PR.

## Solution

Swap `SystemParam::validate_param` from a `bool` to:

```rust
/// The outcome of system / system param validation,
/// used by system executors to determine what to do with a system.
pub enum ValidationOutcome {
    /// All system parameters were validated successfully and the system can be run.
    Valid,
    /// At least one system parameter failed validation, and an error must be handled.
    /// By default, this will result in1 a panic. See [crate::error] for more information.
    ///
    /// This is the default behavior, and is suitable for system params that should *always* be valid,
    /// either because sensible fallback behavior exists (like [`Query`] or because
    /// failures in validation should be considered a bug in the user's logic that must be immediately addressed (like [`Res`]).
    Invalid,
    /// At least one system parameter failed validation, but the system should be skipped due to [`ValidationBehavior::Skip`].
    /// This is suitable for system params that are intended to only operate in certain application states, such as [`Single`].
    Skipped,
}
```
Then, inside of each `SystemParam` implementation, return either Valid,
Invalid or Skipped.

Currently, only `Single`, `Option<Single>` and `Populated` use the
`Skipped` behavior. Other params (like resources) retain their current
failing

## Testing

Messed around with the fallible_params example. Added a pair of tests:
one for panicking when resources are missing, and another for properly
skipping `Single` and `Populated` system params.

## To do

- [x] get https://github.com/bevyengine/bevy/pull/18454 merged
- [x] fix the todo!() in the macro-powered tuple implementation (please
help 🥺)
- [x] test
- [x] write a migration guide
- [x] update the example comments

## Migration Guide

Various system and system parameter validation methods
(`SystemParam::validate_param`, `System::validate_param` and
`System::validate_param_unsafe`) now return and accept a
`ValidationOutcome` enum, rather than a `bool`. The previous `true`
values map to `ValidationOutcome::Valid`, while `false` maps to
`ValidationOutcome::Invalid`.

However, if you wrote a custom schedule executor, you should now respect
the new `ValidationOutcome::Skipped` parameter, skipping any systems
whose validation was skipped. By contrast, `ValidationOutcome::Invalid`
systems should also be skipped, but you should call the
`default_error_handler` on them first, which by default will result in a
panic.

If you are implementing a custom `SystemParam`, you should consider
whether failing system param validation is an error or an expected
state, and choose between `Invalid` and `Skipped` accordingly. In Bevy
itself, `Single` and `Populated` now once again skip the system when
their conditions are not met. This is the 0.15.0 behavior, but stands in
contrast to the 0.15.1 behavior, where they would panic.

---------

Co-authored-by: MiniaczQ <xnetroidpl@gmail.com>
Co-authored-by: Dmytro Banin <banind@cs.washington.edu>
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-03-25 04:27:20 +00:00
Joona Aalto
206599adf7
Add no_std compatible ceil method (#18498)
# Objective


[`f32::ceil`](https://doc.rust-lang.org/std/primitive.f32.html#method.ceil)
is not available in `core`. We have `floor` in `bevy_math::ops`, but no
equivalent for `floor`.

## Solution

Add `ops::ceil` for `no_std` compatibility.
2025-03-25 04:18:00 +00:00
Erick Z
933752ad46
Don't panic on temporary files in file watcher (#18462)
# Objective

Fixes #18461

Apparently `RustRover` creates a temporary file with a tilde like
`load_scene_example.scn.ron~` and at the moment of calling
`.canonicalize()` the file does not exists anymore.

## Solution

Not call `.unwrap()` and return `None` fixes the issue. 

## Testing

- `cargo ci`: OK
- Tested the `scene` example with `file_watcher` feature and it works as
expected.

Co-authored-by: François Mockers <mockersf@gmail.com>
2025-03-25 04:16:33 +00:00
Greeble
584c6665f9
Reduce dependencies on bevy_render by preferring bevy_mesh imports (#18437)
## Objective

Reduce dependencies on `bevy_render` by preferring `bevy_mesh` imports
over `bevy_render` re-exports.

```diff
- use bevy_render::mesh::Mesh;
+ use bevy_mesh::Mesh;
```

This is intended to help with #18423 (render crate restructure). Affects
`bevy_gltf`, `bevy_animation` and `bevy_picking`.

## But Why?

As part of #18423, I'm assuming there'll be a push to make crates less
dependent on the big render crates. This PR seemed like a small and safe
step along that path - it only changes imports and makes the `bevy_mesh`
crate dependency explicit in `Cargo.toml`. Any remaining dependencies on
`bevy_render` are true dependencies.

## Testing

```
cargo run --example testbed_3d
cargo run --example mesh_picking
```
2025-03-25 04:14:42 +00:00
Kristoffer Søholm
7a37c4a109
Update bincode to 2.0 (#18396)
# Objective

Update bincode

## Solution

Fix compilation for #18352 by reading the [migration
guide](https://github.com/bincode-org/bincode/blob/trunk/docs/migration_guide.md)

Also fixes an unused import warning I got when running the tests for
bevy_reflect.
2025-03-25 04:09:46 +00:00
JMS55
8481b63ed8
Record bloom render commands in parallel (#18330)
Closes https://github.com/bevyengine/bevy/issues/18304.

Requesting that someone more experienced with tracy test performance on
a larger scene please!
2025-03-25 04:06:42 +00:00
Martín Maita
37b62b83c4
Update accesskit and accesskit_winit requirements (#18285)
# Objective

- Fixes #18225

## Solution

-  Updated `accesskit` version requirement from 0.17 to 0.18
-  Updated `accesskit_winit` version requirement from 0.23 to 0.25

## Testing

- Ran CI checks locally.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-25 04:04:28 +00:00
Joshua Maleszewski
751263ddaf
AssetServer out of bounds protection (#18088)
Addresses Issue #18073

---------

Co-authored-by: Threadzless <threadzless@gmail.com>
Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
2025-03-25 04:02:22 +00:00
ickshonpe
390877cdae
ExtractedSprites slice buffer (#17041)
# Objective

Instead of extracting an individual sprite per glyph of a text spawn or
slice of a nine-patched sprite, add a buffer to store the extracted
slice geometry.

Fixes #16972

## Solution

* New struct `ExtractedSlice` to hold sprite slice size, position and
atlas info (for text each glyph is a slice).
* New resource `ExtractedSlices` that wraps the `ExtractedSlice` buffer.
This is a separate resource so it can be used without sprites (with a
text material, for example).
* New enum `ExtractedSpriteKind` with variants `Single` and `Slices`.
`Single` represents a single sprite, `Slices` contains a range into the
`ExtractedSlice` buffer.
* Only queue a single `ExtractedSprite` for sets of glyphs or slices and
push the geometry for each individual slice or glyph into the
`ExtractedSlice` buffer.
* Modify `ComputedTextureSlices` to return an `ExtractedSlice` iterator
instead of `ExtractedSprites`.
* Modify `extract_text2d_sprite` to only queue new `ExtractedSprite`s on
font changes and otherwise push slices.

I don't like the name `ExtractedSpriteKind` much, it's a bit redundant
and too haskellish. But although it's exported, it's not something users
will interact with most of the time so don't want to overthink it.

## Testing
yellow = this pr, red = main

```cargo run --example many_glyphs --release --features "trace_tracy" -- --no-ui```

<img width="454" alt="many-glyphs" src="https://github.com/user-attachments/assets/711b52c9-2d4d-43c7-b154-e81a69c94dce" />

```cargo run --example many_text2d --release --features "trace_tracy"```
<img width="415" alt="many-text2d"
src="https://github.com/user-attachments/assets/5ea2480a-52e0-4cd0-9f12-07405cf6b8fa"
/>

## Migration Guide
* `ExtractedSprite` has a new `kind: ExtractedSpriteKind` field with
variants `Single` and `Slices`.
- `Single` represents a single sprite. `ExtractedSprite`'s `anchor`,
`rect`, `scaling_mode` and `custom_size` fields have been moved into
`Single`.
- `Slices` contains a range that indexes into a new resource
`ExtractedSlices`. Slices are used to draw elements composed from
multiple sprites such as text or nine-patched borders.
* `ComputedTextureSlices::extract_sprites` has been renamed to
`extract_slices`. Its `transform` and `original_entity` parameters have
been removed.

---------

Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
2025-03-25 03:51:50 +00:00
damccull
16dacb0fb6
Update linux_dependencies.md (#18523)
# Objective

The latest version of nixpkgs doesn't include the file, breaking the old
link.

## Solution

Change the nixos packaging example link to a permalink with the latest
known version of the 'jumpy' program.

## Testing

- Did you test these changes? If so, how?
  - No testing needed. Just a link change.
- Are there any parts that need more testing?
  - No
2025-03-25 01:51:18 +00:00
Zachary Harrold
6bb7573a33
Switch from OnceCell to LazyLock in bevy_tasks (#18506)
# Objective

Remove `critical-section` from required dependencies, allowing linking
without any features.

## Solution

- Switched from `OnceCell` to `LazyLock`
- Removed `std` feature from `bevy_dylib` (proof that it works)

## Testing

- CI
2025-03-24 07:43:22 +00:00
andriyDev
08d3b113e0
Delete unused weak_handle INSTANCE_INDEX_SHADER_HANDLE. (#18507)
# Objective

- This variable is unused and never populated. I searched for the
literal text of the const and got no hits.

## Solution

- Delete it!

## Testing

- None.
2025-03-24 07:42:06 +00:00
Alice Cecile
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>
2025-03-24 05:58:05 +00:00
Zachary Harrold
13fdae1694
Address Lints in bevy_reflect (#18479)
# Objective

On Windows there are several unaddressed lints raised by clippy.

## Solution

Addressed them!

## Testing

- CI on Windows & Ubuntu
2025-03-23 23:53:31 +00:00
François Mockers
1ffa8254d4
enable x11 by default in bevy_winit (#18475)
# Objective

- building bevy_winit on linux fails with
```
error: The platform you're compiling for is not supported by winit
  --> /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winit-0.30.9/src/platform_impl/mod.rs:78:1
   |
78 | compile_error!("The platform you're compiling for is not supported by winit");
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```
- this blocks publishing Bevy from linux during the verification step

## Solution

- Enable `x11` by default when building bevy_winit, and disable default
features of bevy_winit in bevy_internal
- This doesn't change anything when depending on Bevy
2025-03-23 23:52:58 +00:00
darth levi
f6578adf7b
register ComputedNodeTarget (#18503)
I had no reference to `ComputedNodeTarget` in my project. After updating
to bevy 0.16.0-rc1 i got a compile error complaining about this.
2025-03-23 23:50:15 +00:00
François Mockers
031f67ebb6
fix error and lints when building for wasm32 (#18500)
# Objective

- Some crates don't compile or have clippy warnings when building for
wasm32

## Solution

- bevy_asset: unused lifetime
- bevy_gltf: the error is not too large in wasm32
- bevy_remote: fails to compile as feature http is also gated on wasm32
- bevy_winit: unused import `error`
2025-03-23 22:06:28 +00:00
Zachary Harrold
dd56d45557
Address lints in bevy_asset (#18502)
# Objective

`cargo clippy -p bevy_asset` warns on a pair of lints on my Windows 10
development machine (return from let binding).

## Solution

Addressed them!

## Testing

- CI
2025-03-23 22:05:12 +00:00
Ida "Iyes
7161e9ca20
Regression fix: Reintroduce sorting/reordering methods on Children (#18476)
# Objective

Bevy 0.15 used to have methods on `Children` for sorting and reordering
them. This is very important, because in certain situations, the order
of children matters. For example, in the context of UI nodes.

These methods are missing/omitted/forgotten in the current version,
after the Relationships rework.

Without them, it is impossible for me to upgrade `iyes_perf_ui` to Bevy
0.16.

## Solution

Reintroduce the methods. This PR simply copy-pastes them from Bevy 0.15.
2025-03-23 22:02:22 +00:00
Joshua Holmes
8d9f948684
Create new NonSendMarker (#18301)
# Objective

Create new `NonSendMarker` that does not depend on `NonSend`.

Required, in order to accomplish #17682. In that issue, we are trying to
replace `!Send` resources with `thread_local!` in order to unblock the
resources-as-components effort. However, when we remove all the `!Send`
resources from a system, that allows the system to run on a thread other
than the main thread, which is against the design of the system. So this
marker gives us the control to require a system to run on the main
thread without depending on `!Send` resources.

## Solution

Create a new `NonSendMarker` to replace the existing one that does not
depend on `NonSend`.

## Testing

Other than running tests, I ran a few examples:
- `window_resizing`
- `wireframe`
- `volumetric_fog` (looks so cool)
- `rotation`
- `button`

There is a Mac/iOS-specific change and I do not have a Mac or iOS device
to test it. I am doubtful that it would cause any problems for 2
reasons:
1. The change is the same as the non-wasm change which I did test
2. The Pixel Eagle tests run Mac tests

But it wouldn't hurt if someone wanted to spin up an example that
utilizes the `bevy_render` crate, which is where the Mac/iSO change was.

## Migration Guide

If `NonSendMarker` is being used from `bevy_app::prelude::*`, replace it
with `bevy_ecs::system::NonSendMarker` or use it from
`bevy_ecs::prelude::*`. In addition to that, `NonSendMarker` does not
need to be wrapped like so:
```rust
fn my_system(_non_send_marker: Option<NonSend<NonSendMarker>>) {
    ...
}
```

Instead, it can be used without any wrappers:
```rust
fn my_system(_non_send_marker: NonSendMarker) {
    ...
}
```

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-03-23 21:37:40 +00:00
Sung-jin Brian Hong
694db96ab8
Fix compile errors on headless example (#18497)
# Objective

- Fixes compile errors on headless example when running `cargo run
--example headless --no-default-features`

```
error[E0432]: unresolved import `bevy::log`
  --> examples/app/headless.rs:13:39
   |
13 | use bevy::{app::ScheduleRunnerPlugin, log::LogPlugin, prelude::*};
   |                                       ^^^ could not find `log` in `bevy`

For more information about this error, try `rustc --explain E0432`.
error: could not compile `bevy` (example "headless") due to 1 previous error
```

## Solution

- Since commit cc69fdd bevy_log has been identified as a separate
feature, thus requiring additional parameters to build headless example.
- Changed the command to run to: `cargo run --example headless
--no-default-features --features bevy_log`

## Testing

- The new command successfully builds the example
2025-03-23 21:24:20 +00:00
ickshonpe
c3f72ba4ad
Skip camera look ups in queue uinodes (#17668)
# Objective

`queue_uinodes` looks up the `ExtractedView` for every extracted UI
node, but there's no need to look it up again if consecutive nodes have
the same `extracted_camera_entity`.

## Solution

In queue uinodes reuse the previously looked up extracted view if the
`extracted_camera_entity` doesn't change

## Showcase

```
cargo run --example many_buttons --release --features "trace_tracy"
```

<img width="521" alt="queue-ui-improvement"
src="https://github.com/user-attachments/assets/2f111837-8c2e-4a6d-94cd-3c3462c58bc9"
/>

yellow is this PR, red is main
2025-03-23 09:42:44 +00:00
krunchington
a090e2fd47
Update shader_prepass, testbed_2d, and first_person_view_model examples to use children! macro (#18270)
# Objective

Contributes to #18238 
Updates the `shader_prepass`, `testbed_2d` and `first_person_view_model`
examples to use the `children!` macro. I wanted to keep the PR small but
chose to do 3 examples since they were all limited in scope

## Solution

Updates examples to use the Improved Spawning API merged in
https://github.com/bevyengine/bevy/pull/17521

## Testing

- Did you test these changes? If so, how?
- Opened the examples before and after and verified the same behavior
was observed. I did this on Ubuntu 24.04.2 LTS using `--features
wayland`.
- Are there any parts that need more testing?
- Other OS's and features can't hurt, but this is such a small change it
shouldn't be a problem.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - Run the examples yourself with and without these changes.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - see above

---

## Showcase

n/a

## Migration Guide

n/a
2025-03-22 22:35:20 +00:00
krunchington
5a9cb7de3e
Update text2d example to use children macro (#18317)
# Objective

Contributes to #18238 
Updates the `text2d`, example to use the `children!` macro.

I'm not sure I love the SpawnIter usage here, as I feel the `move`
keyword in this case is subtle and error prone for those who lose fights
with the borrow checker frequently (like me). Feedback very much
welcome.

## Solution

Updates examples to use the Improved Spawning API merged in
https://github.com/bevyengine/bevy/pull/17521

## Testing

- Did you test these changes? If so, how?
- Opened the examples before and after and verified the same behavior
was observed. I did this on Ubuntu 24.04.2 LTS using `--features
wayland`.
- Are there any parts that need more testing?
- Other OS's and features can't hurt, but this is such a small change it
shouldn't be a problem.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - Run the examples yourself with and without these changes.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - see above

---

## Showcase

n/a

## Migration Guide

n/a
2025-03-22 22:32:40 +00:00
Viktor Gustavsson
0244a841b7
Fix lint errors on bevy_ecs with disabled features (#18488)
# Objective

- `bevy_ecs` has lint errors without some features

## Solution

- Fix `clippy::allow-attributes-without-reason` when `bevy_reflect` is
disabled by adding a reason
- Fix `clippy::needless_return` when `std` is disabled by adding a gated
`expect` attribute and a comment to remove it when the `no_std` stuff is
addressed

## Testing

- `cargo clippy -p bevy_ecs --no-default-features --no-deps -- --D
warnings`
- CI
2025-03-22 16:36:56 +00:00
Viktor Gustavsson
d02a206940
Fix clippy::unnecessary-literal-unwrap in bevy_time (#18485)
# Objective

- Compiling `bevy_time` without the `std`-feature results in a
`clippy::unnecessary-literal-unwrap`.

## Solution

- Fix lint error

## Testing

- CI
---
2025-03-22 13:27:37 +00:00
Zachary Harrold
5ba86654a0
Remove bevy_input_focus from bevy_a11y (#18483)
# Objective

- Compiling `bevy_a11y` without default features fails because you need
to select a floating point backed. But you actually don't need it, this
requirement is from an unused linkage to `bevy_input_focus`

## Solution

- Remove link

## Testing

- CI

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
2025-03-22 12:37:42 +00:00
Zachary Harrold
a8568f7535
Ensure dds enables bevy_core_pipeline/dds in bevy_anti_aliasing (#18484)
# Objective

- Compile failure with `bevy_anti_aliasing` due to `dds` feature not
enabling `bevy_core_pipeline/dds`, causing a public API desync.

## Solution

- Ensured feature is enabled

## Testing

- CI
2025-03-22 12:27:14 +00:00
Zachary Harrold
e3968e2963
Properly gate imports in bevy_scene (#18482)
# Objective

- Some imports are only used with certain features.

## Solution

- Gate them!

## Testing

- CI
2025-03-22 12:22:20 +00:00
Zachary Harrold
2eb836abaf
Fix clippy::let_and_return in bevy_ecs (#18481)
# Objective

- `clippy::let_and_return` fails in `bevy_ecs`

## Solution

- Fixed it!

## Testing

- CI
2025-03-22 11:48:40 +00:00
Zachary Harrold
72b4ed05c7
Address clippy::let_and_return in bevy_utils (#18480)
# Objective

`clippy::let_and_return` fails on Windows.

## Solution

Fixed it!

## Testing

- CI
2025-03-22 11:44:49 +00:00
Zachary Harrold
4127ac1662
Properly gate functionality on http in bevy_remote (#18478)
# Objective

Noticed that `bevy_remote` fails to compile without default features.

## Solution

Adjusted offending method to avoid reliance on `http` module when it is
disabled.

## Testing

- CI
- `cargo clippy -p bevy_remote --no-default-features`
2025-03-22 11:26:36 +00:00
Zachary Harrold
c87eeed6d8
Address lints in bevy_platform_support (#18477)
# Objective

@mockersf noticed there were some failing lints in
`bevy_platform_support`.

## Solution

Addressed the lints!

## Testing

- CI
2025-03-22 11:21:18 +00:00
Brezak
1b82f1fae8
Fix clippy warning about unnecessary return in single_threaded_taks_pool.rs (#18472)
# Objective

Every time I run `cargo clippy -p bevy_ecs` it pops up and it's
distracting.

## Solution

Removed unnecessary returns. The blocks themselves are necessary or the
`#[cfg(...)]` doesn't apply properly

## Testing

`cargo clippy -p bevy_ecs` + ci build tests
2025-03-22 09:03:29 +00:00
kirawulff
716fc8b54b
Fix double-despawning in despawn_world and despawn_world_recursive benchmarks (#18448)
# Objective

- Fixes #18430


## Solution

- Moves world creation into per-iteration setup for both `despawn_world`
and `despawn_world_recursive`, meaning the world's entities don't aren't
despawned multiple times
- Doesn't affect despawn APIs

## Testing

- Tested manually by running `cargo bench -p benches --bench ecs --
despawn_world`
2025-03-22 03:26:34 +00:00
ickshonpe
84b09b9398
Newtype Anchor (#18439)
# Objective

The `Anchor` component doesn't need to be a enum. The variants are just
mapped to `Vec2`s so it could be changed to a newtype with associated
const values, saving the space needed for the discriminator by the enum.

Also there was no benefit I think in hiding the underlying `Vec2`
representation of `Anchor`s.

Suggested by @atlv24.

Fixes #18459
Fixes #18460

## Solution

Change `Anchor` to a struct newtyping a `Vec2`, and its variants into
associated constants.

## Migration Guide

The anchor component has been changed from an enum to a struct newtyping
a `Vec2`. The `Custom` variant has been removed, instead to construct a
custom `Anchor` use its tuple constructor:
```rust
Sprite {
     anchor: Anchor(Vec2::new(0.25, 0.4)),
     ..default()
}
```
The other enum variants have been replaced with corresponding constants:
* `Anchor::BottomLeft` to `Anchor::BOTTOM_LEFT`
* `Anchor::Center` to `Anchor::CENTER`
* `Anchor::TopRight` to `Anchor::TOP_RIGHT`
* .. and so on for the remaining variants
2025-03-21 22:27:11 +00:00
krunchington
9ae7aa4399
Update testbed_ui to use Improved Spawning API (#18329)
# Objective

Contributes to #18238 
Updates the `text2d`, example to use the `children!` macro.

~~The SpawnIter usage in this example is maybe not the best. Very open
to opinions. I even left one `with_children` that I thought was just
much better than any alternative.~~

## Solution

Updates examples to use the Improved Spawning API merged in
https://github.com/bevyengine/bevy/pull/17521

## Testing

- Did you test these changes? If so, how?
- Opened the examples before and after and verified the same behavior
was observed. I did this on Ubuntu 24.04.2 LTS using `--features
wayland`.
- Are there any parts that need more testing?
- Other OS's and features can't hurt, but this is such a small change it
shouldn't be a problem.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - Run the examples yourself with and without these changes.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - see above

---

## Showcase

n/a

## Migration Guide

n/a
2025-03-21 19:32:12 +00:00
Alice Cecile
116484b37b
Fix missed error_handling example rename and update description (#18455)
# Objective

I didn't rename the example properly in the meta data, and the
description is unclear and limited.

## Solution

Fix both of those.
2025-03-21 02:15:37 +00:00
Carter Anderson
a033f1b206
Replace VisitEntities with MapEntities (#18432)
# Objective

There are currently too many disparate ways to handle entity mapping,
especially after #17687. We now have MapEntities, VisitEntities,
VisitEntitiesMut, Component::visit_entities,
Component::visit_entities_mut.

Our only known use case at the moment for these is entity mapping. This
means we have significant consolidation potential.

Additionally, VisitEntitiesMut cannot be implemented for map-style
collections like HashSets, as you cant "just" mutate a `&mut Entity`.
Our current approach to Component mapping requires VisitEntitiesMut,
meaning this category of entity collection isn't mappable. `MapEntities`
is more generally applicable. Additionally, the _existence_ of the
blanket From impl on VisitEntitiesMut blocks us from implementing
MapEntities for HashSets (or any types we don't own), because the owner
could always add a conflicting impl in the future.

## Solution

Use `MapEntities` everywhere and remove all "visit entities" usages.

* Add `Component::map_entities`
* Remove `Component::visit_entities`, `Component::visit_entities_mut`,
`VisitEntities`, and `VisitEntitiesMut`
* Support deriving `Component::map_entities` in `#[derive(Coomponent)]`
* Add `#[derive(MapEntities)]`, and share logic with the
`Component::map_entities` derive.
* Add `ComponentCloneCtx::queue_deferred`, which is command-like logic
that runs immediately after normal clones. Reframe `FromWorld` fallback
logic in the "reflect clone" impl to use it. This cuts out a lot of
unnecessary work and I think justifies the existence of a pseudo-command
interface (given how niche, yet performance sensitive this is).

Note that we no longer auto-impl entity mapping for ` IntoIterator<Item
= &'a Entity>` types, as this would block our ability to implement cases
like `HashMap`. This means the onus is on us (or type authors) to add
explicit support for types that should be mappable.

Also note that the Component-related changes do not require a migration
guide as there hasn't been a release with them yet.

## Migration Guide

If you were previously implementing `VisitEntities` or
`VisitEntitiesMut` (likely via a derive), instead use `MapEntities`.
Those were almost certainly used in the context of Bevy Scenes or
reflection via `ReflectMapEntities`. If you have a case that uses
`VisitEntities` or `VisitEntitiesMut` directly, where `MapEntities` is
not a viable replacement, please let us know!

```rust
// before
#[derive(VisitEntities, VisitEntitiesMut)]
struct Inventory {
  items: Vec<Entity>,
  #[visit_entities(ignore)]
  label: String,
}

// after
#[derive(MapEntities)]
struct Inventory {
  #[entities]
  items: Vec<Entity>,
  label: String,
}
```
2025-03-21 00:18:10 +00:00
Wuketuke
55fd10502c
Required components accept const values (#16720) (#18309)
# Objective

Const values should be more ergonomic to insert, since this is too
verbose
``` rust
#[derive(Component)]
#[require(
    LockedAxes(||LockedAxes::ROTATION_LOCKED),
)]
pub struct CharacterController;
```
instead, users can now abbreviate that nonsense like this
``` rust
#[derive(Component)]
#[require(
    LockedAxes = ROTATION_LOCKED),
)]
pub struct CharacterController;
```
it also works for enum labels.
I chose to omit the type, since were trying to reduce typing here. The
alternative would have been this:
```rust
#[require(
    LockedAxes = LockedAxes::ROTATION_LOCKED),
)]
```
This of course has its disadvantages, since the const has to be
associated, but the old closure method is still possible, so I dont
think its a problem.
- Fixes #16720

## Testing

I added one new test in the docs, which also explain the new change. I
also saw that the docs for the required components on line 165 was
missing an assertion, so I added it back in

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-03-21 00:02:10 +00:00
krunchington
dac0b1019a
Update sprite_slice, spatial_audio_3d, spatial_audio_2d examples to use children macro (#18318)
# Objective

Contributes to #18238 
Updates the `sprite_slice`, `spatial_audio_3d` and `spatial_audio_2d`
examples to use the `children!` macro.

## Solution

Updates examples to use the Improved Spawning API merged in
https://github.com/bevyengine/bevy/pull/17521

## Testing

- Did you test these changes? If so, how?
- Opened the examples before and after and verified the same behavior
was observed. I did this on Ubuntu 24.04.2 LTS using `--features
wayland`.
- Are there any parts that need more testing?
- Other OS's and features can't hurt, but this is such a small change it
shouldn't be a problem.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - Run the examples yourself with and without these changes.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - see above

---

## Showcase

n/a

## Migration Guide

n/a
2025-03-20 23:38:12 +00:00
Lucas Franca
acd4451151
Fix warning spam on mesh2d_manual example (#18433)
# Objective

Fixes #18429 

## Solution

Add syncing to the render world for the `ColoredMesh2d` component

## Testing

Ran the example and it works as intended without the warning spam
2025-03-20 20:05:57 +00:00
kirawulff
398e011778
Fix incorrect command given by the benchmarking README (#18431)
Note: Fixing this caused me to open #18430. On the whole, this fix and
that bug don't really depend on each other, so I'm opening this PR
anyways

# Objective

- Fixes #18387 

## Solution

- Very small update to benchmarking documentation
- Checked through to ensure consistency with other documentation. The
only other mention of benchmarking commands I could find is a comment in
the `Cargo.toml` associated with the benchmarking; the correct command
is already listed there.

## Testing

- Manual testing on command line using updated commands
- Caused me to see #18430
2025-03-20 20:05:02 +00:00
Alice Cecile
d837b9e19b
Establish structure and process for in-repo release notes (#18427)
# Objective

As discussed in #https://github.com/bevyengine/bevy/discussions/16431,
our release process is a major bottleneck, difficult to collaborate on
and a serious source of burnout (hi!). Writing all of the release notes
and migration guides at the end of the cycle is miserable, and makes it
harder to coalesce them across multiple PRs doing related work.

## Solution

This PR largely implements the solution designed and discussed in the
[Better release
notes](https://discord.com/channels/691052431525675048/1331412459432710247)
working group, unofficially led by @NthTensor. The
[plan](https://hackmd.io/NBKkrGbbS5CaU7PsQUFGJQ) laid out in the linked
HackMD has largely been folllowed, although I've also added Migration
Guides too: they suffer much the same problem.

I've also moved away from using the PR number as the title for the file:
if we're hand-authoring the files we can do much better than that!

The READMEs for each folder describe the process in more detail: please
read (and comment on them!).

## Questions for reviewers / fellow implementers

- I've moved away from the current "store metadata in a mega-file"
approach, and moved it towards a "put the metadata in the file you're
editing" design. I much prefer the locality, but it may be harder to get
to play nice with our website generation. Do you want me to revert that?
See [this
folder](https://github.com/bevyengine/bevy-website/tree/main/release-content/0.15)
for the current format.
- does the "how to write release notes / migration guides" sections make
sense to you here? Normally I would toss this in the Contributor's
Guide, but I do like it being right beside the spot you're making
changes

## Follow-up work

- [ ] add a job which checks for the labels and blocks the PR if no file
in the correct folder is merged
- [ ] add a CI job to do basic format validation on the files inside of
these folders
- [ ] generate the release notes and migration guides using a modified
version of [the
tooling](https://github.com/bevyengine/bevy-website/tree/main/generate-release)
one last time for 0.16
- [ ] remove the release notes and migration guide generating tools
- [ ] build a new pipeline to turn the copy-pasted release notes here
into actual website content
- [ ] rethink how we're doing release notes to provide better punchy
summaries for casual readers
- [ ] create a CI job that checks for new or edited migration guides and
automatically labels the PR

---------

Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2025-03-20 04:26:20 +00:00
Brezak
90ce1ee07c
Add more methods to RelationshipSourceCollection (#18421)
# Objective

While working on #18058 I realized I could use
`RelationshipTargetCollection::new`, so I added it.

## Solution

- Add `RelationshipTargetCollection::new`
- Add `RelationshipTargetCollection::reserve`. Could generally be useful
when doing micro-optimizations.
- Add `RelationshipTargetCollection::shrink_to_fit`. Rust collections
generally don't shrink when removing elements. Might be a good idea to
call this once in a while.

## Testing

`cargo clippy`

---

## Showcase

`RelationshipSourceCollection` now implements `new`, `reserve` and
`shrink_to_fit` to give greater control over how much memory it
consumes.

## Migration Guide

Any type implementing `RelationshipSourceCollection` now needs to also
implement `new`, `reserve` and `shrink_to_fit`. `reserve` and
`shrink_to_fit` can be made no-ops if they conceptually mean nothing to
a collection.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-03-20 01:35:51 +00:00
robtfm
655ee4b120
Gltf handle missing bindposes (#18419)
# Objective

correctly load gltfs without explicit bindposes

## Solution

use identity matrices if bindposes are not found.

note: currently does nothing, as gltfs without explicit bindposes fail
to load, see <https://github.com/gltf-rs/gltf/pull/449>

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-03-19 21:45:05 +00:00
Brezak
28907ae171
Add methods to bulk replace relationships on a entity (#18058)
# Objective

Add a way to efficiently replace a set of specifically related entities
with a new set.
Closes #18041 

## Solution

Add new `replace_(related/children)` to `EntityWorldMut` and friends.

## Testing

Added a new test to `hierarchy.rs` that specifically check if
`replace_children` actually correctly replaces the children on a entity
while keeping the original one.

---

## Showcase

`EntityWorldMut` and `EntityCommands` can now be used to efficiently
replace the entities a entity is related to.

```rust
/// `parent` has 2 children. `entity_a` and `entity_b`.
assert_eq!([entity_a, entity_b], world.entity(parent).get::<Children>());

/// Replace `parent`s children with `entity_a` and `entity_c`
world.entity_mut(parent).replace_related(&[entity_a, entity_c]);

/// `parent` now has 2 children. `entity_a` and `entity_c`.
///
/// `replace_children` has saved time by not removing and reading
/// the relationship between `entity_a` and `parent`
assert_eq!([entity_a, entity_c], world.entity(parent).get::<Children>());

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-03-19 20:04:42 +00:00
Greeble
2aaac934b5
Fix bevy_ecs doc tests with --all-features (#18424)
## Objective

Fix `bevy_ecs` doc tests failing when used with `--all-features`.

```
---- crates\bevy_ecs\src\error\handler.rs - error::handler::GLOBAL_ERROR_HANDLER (line 87) stdout ----
error[E0425]: cannot find function `default_error_handler` in this scope
 --> crates\bevy_ecs\src\error\handler.rs:92:24
  |
8 |    let error_handler = default_error_handler();
  |                        ^^^^^^^^^^^^^^^^^^^^^ not found in this scope
```

I happened to come across this while testing #12207. I'm not sure it
actually needs fixing but seemed worth a go

## Testing

```
cargo test --doc -p bevy_ecs --all-features
```

## Side Notes

The CI misses this error as it doesn't use `--all-features`. Perhaps it
should?

I tried adding `--all-features` to `ci/src/commands/doc_tests.rs` but
this triggered a linker error:

```
Compiling bevy_dylib v0.16.0-dev (C:\Projects\bevy\crates\bevy_dylib)
error: linking with `link.exe` failed: exit code: 1189
= note: LINK : fatal error LNK1189: library limit of 65535 objects exceeded␍
```
2025-03-19 20:02:33 +00:00
Asier Illarramendi
faef6d18e2
Split example file docblock and code when generating web examples markdown (#18191)
# Objective

Separate example explanation (file docblock) and the code so they can be
layout differently in the website and we can give a higher importance to
the explanation on the [website search
tool](https://github.com/bevyengine/bevy-website/pull/1935). This would
also allow us to improve the examples so they become even more like a
cookbook.

## Solution

Update the `example-showcase` tool to extract the example file docblock
and write it as the example markdown content. This allows us to access
the explanation via `page.content` in Zola.

## Testing

I've checked that the output is correct after running the tool and it
doesn't throw any error. I've also validated that the approach will work
on the website.

## Showcase

This is a quick and dirty example of what we could do in the web
examples after the change. When we implement the real thing we can put
the explanation on a sidebar or explore other layout options.

<img width="1362" alt="image"
src="https://github.com/user-attachments/assets/6738542e-31c3-41cd-972a-7fa2e942e85d"
/>
2025-03-19 20:01:37 +00:00
IceSentry
06bae05ba2
Add bevy_anti_aliasing (#18323)
# Objective

- bevy_core_pipeline is getting really big and it's a big bottleneck for
compilation time. A lot of parts of it can be broken up

## Solution

- Add a new bevy_anti_aliasing crate that contains all the anti_aliasing
implementations
- I didn't move any MSAA related code to this new crate because that's a
lot more invasive

## Testing

- Tested the anti_aliasing example to make sure all methods still worked

---

## Showcase

 before:

![image](https://github.com/user-attachments/assets/eac18276-2cb9-41c9-aaf4-a5da643a7ba7)

after:

![image](https://github.com/user-attachments/assets/59cb2fb4-306f-4e42-b156-d5534da5685d)

Notice that now bevy_core_pipeline is 1s shorter and bevy_anti_aliasing
now compiles in parallel with bevy_pbr.

## Migration Guide

When using anti aliasing features, you now need to import them from
`bevy::anti_aliasing` instead of `bevy::core_pipeline`
2025-03-19 18:40:32 +00:00
JMS55
f353cc3340
Fix specialize_shadows system ordering (#18412)
# Objective
- Fixes https://github.com/bevyengine/bevy/issues/18332

## Solution

- Move specialize_shadows to ManageViews so that it can run after
prepare_lights, so that shadow views exist for specialization.
- Unfortunately this means that specialize_shadows is no longer in
PrepareMeshes like the rest of the specialization systems.

## Testing
- Ran anti_aliasing example, switched between the different AA options,
observed no glitches.
2025-03-19 06:40:45 +00:00
charlotte
8d5474a2f2
Fix unecessary specialization checks for apps with many materials (#18410)
# Objective

For materials that aren't being used or a visible entity doesn't have an
instance of, we were unnecessarily constantly checking whether they
needed specialization, saying yes (because the material had never been
specialized for that entity), and failing to look up the material
instance.

## Solution

If an entity doesn't have an instance of the material, it can't possibly
need specialization, so exit early before spending time doing the check.

Fixes #18388.
2025-03-19 06:22:39 +00:00
François Mockers
a5ba2ed036
enable std when building bevy_dylib (#18405)
# Objective

- bevy_dylib fails to build:
```
   Compiling bevy_dylib v0.16.0-rc.1 (/bevy/crates/bevy_dylib)
error: linking with `cc` failed: exit status: 1
  |
  = note: some arguments are omitted. use `--verbose` to show all linker arguments
  = note: Undefined symbols for architecture arm64:
            "__critical_section_1_0_acquire", referenced from:
                critical_section::with::h00cfbe529dea9dc9 in libbevy_tasks-53c9db6a3865f250.rlib[58](bevy_tasks-53c9db6a3865f250.evom2xwveqp508omiiqb25xig.rcgu.o)
            "__critical_section_1_0_release", referenced from:
                core::ptr::drop_in_place$LT$critical_section..with..Guard$GT$::hfa034e0208e1a49d in libbevy_tasks-53c9db6a3865f250.rlib[48](bevy_tasks-53c9db6a3865f250.d9dwgpd0156zfn2h5z5ff94zn.rcgu.o)
          ld: symbol(s) not found for architecture arm64
          clang: error: linker command failed with exit code 1 (use -v to see invocation)
```

## Solution

- enable `std` when building bevy_dylib

---------

Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2025-03-19 00:49:26 +00:00
François Mockers
a24691a115
gate import on bevy_animation in bevy_gltf (#18403)
# Objective

- `collect_path` is only declared when feature `bevy_animation` is
enabled
- it is imported without checking for the feature, not compiling when
not enabled

## Solution

- Gate the import
2025-03-18 23:27:46 +00:00
Carter Weinberg
821f6fa0dd
Small Docs PR for Deferred Worlds (#18384)
# Objective

I was looking over a PR yesterday, and got confused by the docs on
deferred world. I thought I would add a little more detail to the struct
in order to clarify it a little.

## Solution

Document some more about deferred worlds.
2025-03-18 20:30:49 +00:00
Alice Cecile
5ab0456f61
Unified picking cleanup (#18401)
# Objective

@cart noticed some issues with my work in
https://github.com/bevyengine/bevy/pull/17348#discussion_r2001815637,
which I somehow missed before merging the PR.

## Solution

- feature gate the UiPickingPlugin correctly
- don't manually add the picking plugins

## Testing

Ran the debug_picking and sprite_picking examples (for UI and sprites
respectively): both seem to work fine.
2025-03-18 20:28:03 +00:00
Eagster
339914b0af
Fix dynamic scene resources not being entity mapped (#18395)
# Objective

The resources were converted via `clone_reflect_value` and the cloned
value was mapped. But the value that is inserted is the source of the
clone, which was not mapped.

I ran into this issue while working on #18380. Having non consecutive
entity allocations has caught a lot of bugs.

## Solution

Use the cloned value for insertion if it exists.
2025-03-18 20:04:55 +00:00
Alice Cecile
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.


![image](https://github.com/user-attachments/assets/237f644a-b36d-4332-9b45-76fd5cbff4d0)

---------

Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2025-03-18 19:27:50 +00:00
Antony
65e289f5bc
Unify picking backends (#17348)
# Objective

Currently, our picking backends are inconsistent:

- Mesh picking and sprite picking both have configurable opt in/out
behavior. UI picking does not.
- Sprite picking uses `SpritePickingCamera` and `Pickable` for control,
but mesh picking uses `RayCastPickable`.
- `MeshPickingPlugin` is not a part of `DefaultPlugins`.
`SpritePickingPlugin` and `UiPickingPlugin` are.

## Solution

- Add configurable opt in/out behavior to UI picking (defaults to opt
out).
- Replace `RayCastPickable` with `MeshPickingCamera` and `Pickable`.
- Remove `SpritePickingPlugin` and `UiPickingPlugin` from
`DefaultPlugins`.

## Testing

Ran some examples.

## Migration Guide

`UiPickingPlugin` and `SpritePickingPlugin` are no longer included in
`DefaultPlugins`. They must be explicitly added.

`RayCastPickable` has been replaced in favor of the `MeshPickingCamera`
and `Pickable` components. You should add them to cameras and entities,
respectively, if you have `MeshPickingSettings::require_markers` set to
`true`.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-03-18 19:24:43 +00:00
ickshonpe
b3ccc623fa
Remove commented-out code (#18392)
# Objective

Remove a leftover commented-out line.
2025-03-18 12:46:29 +00:00
1063 changed files with 49616 additions and 22901 deletions

2
.github/FUNDING.yml vendored
View File

@ -1 +1 @@
custom: https://bevyengine.org/donate/
custom: https://bevy.org/donate/

View File

@ -10,4 +10,4 @@ assignees: ''
Provide a link to the documentation and describe how it could be improved. In what ways is it incomplete, incorrect, or misleading?
If you have suggestions on exactly what the new docs should say, feel free to include them here. Alternatively, make the changes yourself and [create a pull request](https://bevyengine.org/learn/contribute/helping-out/writing-docs/) instead.
If you have suggestions on exactly what the new docs should say, feel free to include them here. Alternatively, make the changes yourself and [create a pull request](https://bevy.org/learn/contribute/helping-out/writing-docs/) instead.

View File

@ -36,11 +36,3 @@ println!("My super cool code.");
```
</details>
## Migration Guide
> This section is optional. If there are no breaking changes, you can delete this section.
- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change.

View File

@ -12,19 +12,63 @@ permissions:
pull-requests: 'write'
jobs:
comment-on-breaking-change-label:
comment-on-migration-guide-label:
runs-on: ubuntu-latest
if: github.event.label.name == 'M-Needs-Migration-Guide' && !contains(github.event.pull_request.body, '## Migration Guide')
if: github.event.label.name == 'M-Needs-Migration-Guide'
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 2
- name: Get changes
id: get_changes
shell: bash {0}
run: |
git fetch --depth=1 origin $BASE_SHA
git diff --exit-code $BASE_SHA $HEAD_SHA -- ./release-content/migration-guides
echo "found_changes=$?" >> $GITHUB_OUTPUT
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
- uses: actions/github-script@v7
if: steps.get_changes.outputs.found_changes == '0'
with:
script: |
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `It looks like your PR is a breaking change, but you didn't provide a migration guide.
body: `It looks like your PR is a breaking change, but **you didn't provide a migration guide**.
Could you add some context on what users should update when this change get released in a new version of Bevy?
It will be used to help writing the migration guide for the version. Putting it after a \`## Migration Guide\` will help it get automatically picked up by our tooling.`
Please review the [instructions for writing migration guides](https://github.com/bevyengine/bevy/tree/main/release-content/migration_guides.md), then expand or revise the content in the [migration guides directory](https://github.com/bevyengine/bevy/tree/main/release-content/migration-guides) to reflect your changes.`
})
comment-on-release-note-label:
runs-on: ubuntu-latest
if: github.event.label.name == 'M-Needs-Release-Note'
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 2
- name: Get changes
id: get_changes
shell: bash {0}
run: |
git fetch --depth=1 origin $BASE_SHA
git diff --exit-code $BASE_SHA $HEAD_SHA -- ./release-content/release-notes
echo "found_changes=$?" >> $GITHUB_OUTPUT
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
- uses: actions/github-script@v7
if: steps.get_changes.outputs.found_changes == '0'
with:
script: |
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `It looks like your PR has been selected for a highlight in the next release blog post, but **you didn't provide a release note**.
Please review the [instructions for writing release notes](https://github.com/bevyengine/bevy/tree/main/release-content/release_notes.md), then expand or revise the content in the [release notes directory](https://github.com/bevyengine/bevy/tree/main/release-content/release_notes) to showcase your changes.`
})

View File

@ -15,6 +15,7 @@ env:
# If nightly is breaking CI, modify this variable to target a specific nightly version.
NIGHTLY_TOOLCHAIN: nightly
RUSTFLAGS: "-D warnings"
BINSTALL_VERSION: "v1.12.3"
concurrency:
group: ${{github.workflow}}-${{github.ref}}
@ -94,7 +95,7 @@ jobs:
- name: CI job
# To run the tests one item at a time for troubleshooting, use
# cargo --quiet test --lib -- --list | sed 's/: test$//' | MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation" xargs -n1 cargo miri test -p bevy_ecs --lib -- --exact
run: cargo miri test -p bevy_ecs
run: cargo miri test -p bevy_ecs --features bevy_utils/debug
env:
# -Zrandomize-layout makes sure we dont rely on the layout of anything that might change
RUSTFLAGS: -Zrandomize-layout
@ -246,7 +247,7 @@ jobs:
- name: Check wasm
run: cargo check --target wasm32-unknown-unknown -Z build-std=std,panic_abort
env:
RUSTFLAGS: "-C target-feature=+atomics,+bulk-memory -D warnings"
RUSTFLAGS: "-C target-feature=+atomics,+bulk-memory"
markdownlint:
runs-on: ubuntu-latest
@ -271,9 +272,9 @@ jobs:
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: cargo-bins/cargo-binstall@v1.12.3
- name: Install taplo
run: cargo install taplo-cli --locked
run: cargo binstall taplo-cli@0.9.3 --locked
- name: Run Taplo
id: taplo
run: taplo fmt --check --diff
@ -292,7 +293,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Check for typos
uses: crate-ci/typos@v1.30.2
uses: crate-ci/typos@v1.33.1
- name: Typos info
if: failure()
run: |

View File

@ -82,7 +82,7 @@ jobs:
- name: Finalize documentation
run: |
echo "<meta http-equiv=\"refresh\" content=\"0; url=bevy/index.html\">" > target/doc/index.html
echo "dev-docs.bevyengine.org" > target/doc/CNAME
echo "dev-docs.bevy.org" > target/doc/CNAME
echo $'User-Agent: *\nDisallow: /' > target/doc/robots.txt
rm target/doc/.lock

View File

@ -49,7 +49,8 @@ jobs:
--exclude ci \
--exclude errors \
--exclude bevy_mobile_example \
--exclude build-wasm-example
--exclude build-wasm-example \
--exclude no_std_library
- name: Create PR
uses: peter-evans/create-pull-request@v7

View File

@ -43,5 +43,5 @@ jobs:
repo: context.repo.repo,
body: `**Welcome**, new contributor!
Please make sure you've read our [contributing guide](https://bevyengine.org/learn/contribute/introduction) and we look forward to reviewing your pull request shortly ✨`
Please make sure you've read our [contributing guide](https://bevy.org/learn/contribute/introduction) and we look forward to reviewing your pull request shortly ✨`
})

View File

@ -1,4 +1,4 @@
# Contributing to Bevy
If you'd like to help build Bevy, start by reading this
[introduction](https://bevyengine.org/learn/contribute/introduction). Thanks for contributing!
[introduction](https://bevy.org/learn/contribute/introduction). Thanks for contributing!

View File

@ -1,16 +1,16 @@
[package]
name = "bevy"
version = "0.16.0-dev"
version = "0.17.0-dev"
edition = "2024"
categories = ["game-engines", "graphics", "gui", "rendering"]
description = "A refreshingly simple data-driven game engine and app framework"
exclude = ["assets/", "tools/", ".github/", "crates/", "examples/wasm/assets/"]
homepage = "https://bevyengine.org"
homepage = "https://bevy.org"
keywords = ["game", "engine", "gamedev", "graphics", "bevy"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/bevyengine/bevy"
documentation = "https://docs.rs/bevy"
rust-version = "1.85.0"
rust-version = "1.86.0"
[workspace]
resolver = "2"
@ -133,6 +133,8 @@ default = [
"bevy_audio",
"bevy_color",
"bevy_core_pipeline",
"bevy_core_widgets",
"bevy_anti_aliasing",
"bevy_gilrs",
"bevy_gizmos",
"bevy_gltf",
@ -162,6 +164,7 @@ default = [
"vorbis",
"webgl2",
"x11",
"debug",
]
# Recommended defaults for no_std applications
@ -213,6 +216,13 @@ bevy_core_pipeline = [
"bevy_render",
]
# Provides various anti aliasing solutions
bevy_anti_aliasing = [
"bevy_internal/bevy_anti_aliasing",
"bevy_asset",
"bevy_render",
]
# Adds gamepad support
bevy_gilrs = ["bevy_internal/bevy_gilrs"]
@ -225,6 +235,7 @@ bevy_pbr = [
"bevy_asset",
"bevy_render",
"bevy_core_pipeline",
"bevy_anti_aliasing",
]
# Provides picking functionality
@ -236,12 +247,22 @@ bevy_render = ["bevy_internal/bevy_render", "bevy_color"]
# Provides scene functionality
bevy_scene = ["bevy_internal/bevy_scene", "bevy_asset"]
# Provides raytraced lighting (experimental)
bevy_solari = [
"bevy_internal/bevy_solari",
"bevy_asset",
"bevy_core_pipeline",
"bevy_pbr",
"bevy_render",
]
# Provides sprite functionality
bevy_sprite = [
"bevy_internal/bevy_sprite",
"bevy_render",
"bevy_core_pipeline",
"bevy_color",
"bevy_anti_aliasing",
]
# Provides text functionality
@ -254,6 +275,7 @@ bevy_ui = [
"bevy_text",
"bevy_sprite",
"bevy_color",
"bevy_anti_aliasing",
]
# Windowing layer
@ -280,6 +302,9 @@ bevy_log = ["bevy_internal/bevy_log"]
# Enable input focus subsystem
bevy_input_focus = ["bevy_internal/bevy_input_focus"]
# Headless widget collection for Bevy UI.
bevy_core_widgets = ["bevy_internal/bevy_core_widgets"]
# Enable passthrough loading for SPIR-V shaders (Only supported on Vulkan, shader capabilities and extensions must agree with the platform implementation)
spirv_shader_passthrough = ["bevy_internal/spirv_shader_passthrough"]
@ -482,7 +507,10 @@ file_watcher = ["bevy_internal/file_watcher"]
embedded_watcher = ["bevy_internal/embedded_watcher"]
# Enable stepping-based debugging of Bevy systems
bevy_debug_stepping = ["bevy_internal/bevy_debug_stepping"]
bevy_debug_stepping = [
"bevy_internal/bevy_debug_stepping",
"bevy_internal/debug",
]
# Enables the meshlet renderer for dense high-poly scenes (experimental)
meshlet = ["bevy_internal/meshlet"]
@ -523,30 +551,36 @@ libm = ["bevy_internal/libm"]
# Enables use of browser APIs. Note this is currently only applicable on `wasm32` architectures.
web = ["bevy_internal/web"]
# Enable hotpatching of Bevy systems
hotpatching = ["bevy_internal/hotpatching"]
# Enable collecting debug information about systems and components to help with diagnostics
debug = ["bevy_internal/debug"]
[dependencies]
bevy_internal = { path = "crates/bevy_internal", version = "0.16.0-dev", default-features = false }
bevy_internal = { path = "crates/bevy_internal", version = "0.17.0-dev", default-features = false }
tracing = { version = "0.1", default-features = false, optional = true }
# Wasm does not support dynamic linking.
[target.'cfg(not(target_family = "wasm"))'.dependencies]
bevy_dylib = { path = "crates/bevy_dylib", version = "0.16.0-dev", default-features = false, optional = true }
bevy_dylib = { path = "crates/bevy_dylib", version = "0.17.0-dev", default-features = false, optional = true }
[dev-dependencies]
rand = "0.8.0"
rand_chacha = "0.3.1"
ron = "0.8.0"
ron = "0.10"
flate2 = "1.0"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_json = "1.0.140"
bytemuck = "1.7"
bevy_render = { path = "crates/bevy_render", version = "0.16.0-dev", default-features = false }
bevy_render = { path = "crates/bevy_render", version = "0.17.0-dev", default-features = false }
# The following explicit dependencies are needed for proc macros to work inside of examples as they are part of the bevy crate itself.
bevy_ecs = { path = "crates/bevy_ecs", version = "0.16.0-dev", default-features = false }
bevy_state = { path = "crates/bevy_state", version = "0.16.0-dev", default-features = false }
bevy_asset = { path = "crates/bevy_asset", version = "0.16.0-dev", default-features = false }
bevy_reflect = { path = "crates/bevy_reflect", version = "0.16.0-dev", default-features = false }
bevy_image = { path = "crates/bevy_image", version = "0.16.0-dev", default-features = false }
bevy_gizmos = { path = "crates/bevy_gizmos", version = "0.16.0-dev", default-features = false }
bevy_ecs = { path = "crates/bevy_ecs", version = "0.17.0-dev", default-features = false }
bevy_state = { path = "crates/bevy_state", version = "0.17.0-dev", default-features = false }
bevy_asset = { path = "crates/bevy_asset", version = "0.17.0-dev", default-features = false }
bevy_reflect = { path = "crates/bevy_reflect", version = "0.17.0-dev", default-features = false }
bevy_image = { path = "crates/bevy_image", version = "0.17.0-dev", default-features = false }
bevy_gizmos = { path = "crates/bevy_gizmos", version = "0.17.0-dev", default-features = false }
# Needed to poll Task examples
futures-lite = "2.0.1"
async-std = "1.13"
@ -558,7 +592,7 @@ hyper = { version = "1", features = ["server", "http1"] }
http-body-util = "0.1"
anyhow = "1"
macro_rules_attribute = "0.2"
accesskit = "0.17"
accesskit = "0.19"
nonmax = "0.5"
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
@ -571,6 +605,17 @@ ureq = { version = "3.0.8", features = ["json"] }
wasm-bindgen = { version = "0.2" }
web-sys = { version = "0.3", features = ["Window"] }
[[example]]
name = "context_menu"
path = "examples/usage/context_menu.rs"
doc-scrape-examples = true
[package.metadata.example.context_menu]
name = "Context Menu"
description = "Example of a context menu"
category = "Usage"
wasm = true
[[example]]
name = "hello_world"
path = "examples/hello_world.rs"
@ -1231,6 +1276,18 @@ description = "Load a cubemap texture onto a cube like a skybox and cycle throug
category = "3D Rendering"
wasm = false
[[example]]
name = "solari"
path = "examples/3d/solari.rs"
doc-scrape-examples = true
required-features = ["bevy_solari"]
[package.metadata.example.solari]
name = "Solari"
description = "Demonstrates realtime dynamic global illumination rendering using Bevy Solari."
category = "3D Rendering"
wasm = false
[[example]]
name = "spherical_area_lights"
path = "examples/3d/spherical_area_lights.rs"
@ -1578,6 +1635,7 @@ wasm = true
name = "headless"
path = "examples/app/headless.rs"
doc-scrape-examples = true
required-features = ["bevy_log"]
[package.metadata.example.headless]
name = "Headless"
@ -2046,6 +2104,7 @@ wasm = false
name = "dynamic"
path = "examples/ecs/dynamic.rs"
doc-scrape-examples = true
required-features = ["debug"]
[package.metadata.example.dynamic]
name = "Dynamic ECS"
@ -2208,14 +2267,14 @@ category = "ECS (Entity Component System)"
wasm = false
[[example]]
name = "fallible_systems"
path = "examples/ecs/fallible_systems.rs"
name = "error_handling"
path = "examples/ecs/error_handling.rs"
doc-scrape-examples = true
required-features = ["bevy_mesh_picking_backend"]
[package.metadata.example.fallible_systems]
name = "Fallible Systems"
description = "Systems that return results to handle errors"
[package.metadata.example.error_handling]
name = "Error handling"
description = "How to return and handle errors across the ECS"
category = "ECS (Entity Component System)"
wasm = false
@ -2289,6 +2348,17 @@ description = "Pipe the output of one system into a second, allowing you to hand
category = "ECS (Entity Component System)"
wasm = false
[[example]]
name = "state_scoped"
path = "examples/ecs/state_scoped.rs"
doc-scrape-examples = true
[package.metadata.example.state_scoped]
name = "State Scoped"
description = "Shows how to spawn entities that are automatically despawned either when entering or exiting specific game states."
category = "ECS (Entity Component System)"
wasm = false
[[example]]
name = "system_closure"
path = "examples/ecs/system_closure.rs"
@ -3328,6 +3398,17 @@ description = "Illustrates creating and updating text"
category = "UI (User Interface)"
wasm = true
[[example]]
name = "text_background_colors"
path = "examples/ui/text_background_colors.rs"
doc-scrape-examples = true
[package.metadata.example.text_background_colors]
name = "Text Background Colors"
description = "Demonstrates text background colors"
category = "UI (User Interface)"
wasm = true
[[example]]
name = "text_debug"
path = "examples/ui/text_debug.rs"
@ -3384,6 +3465,28 @@ description = "An example for CSS Grid layout"
category = "UI (User Interface)"
wasm = true
[[example]]
name = "gradients"
path = "examples/ui/gradients.rs"
doc-scrape-examples = true
[package.metadata.example.gradients]
name = "Gradients"
description = "An example demonstrating gradients"
category = "UI (User Interface)"
wasm = true
[[example]]
name = "stacked_gradients"
path = "examples/ui/stacked_gradients.rs"
doc-scrape-examples = true
[package.metadata.example.stacked_gradients]
name = "Stacked Gradients"
description = "An example demonstrating stacked gradients"
category = "UI (User Interface)"
wasm = true
[[example]]
name = "scroll"
path = "examples/ui/scroll.rs"
@ -3472,6 +3575,17 @@ description = "Illustrates how to use 9 Slicing for TextureAtlases in UI"
category = "UI (User Interface)"
wasm = true
[[example]]
name = "ui_transform"
path = "examples/ui/ui_transform.rs"
doc-scrape-examples = true
[package.metadata.example.ui_transform]
name = "UI Transform"
description = "An example demonstrating how to translate, rotate and scale UI elements."
category = "UI (User Interface)"
wasm = true
[[example]]
name = "viewport_debug"
path = "examples/ui/viewport_debug.rs"
@ -3483,6 +3597,17 @@ description = "An example for debugging viewport coordinates"
category = "UI (User Interface)"
wasm = true
[[example]]
name = "viewport_node"
path = "examples/ui/viewport_node.rs"
doc-scrape-examples = true
[package.metadata.example.viewport_node]
name = "Viewport Node"
description = "Demonstrates how to create a viewport node with picking support"
category = "UI (User Interface)"
wasm = true
# Window
[[example]]
name = "clear_color"
@ -3844,6 +3969,16 @@ description = "A simple 2D screen shake effect"
category = "Camera"
wasm = true
[[example]]
name = "2d_on_ui"
path = "examples/camera/2d_on_ui.rs"
doc-scrape-examples = true
[package.metadata.example.2d_on_ui]
name = "2D on Bevy UI"
description = "Shows how to render 2D objects on top of Bevy UI"
category = "Camera"
wasm = true
[package.metadata.example.fps_overlay]
name = "FPS overlay"
@ -4301,3 +4436,59 @@ name = "`no_std` Compatible Library"
description = "Example library compatible with `std` and `no_std` targets"
category = "Embedded"
wasm = true
[[example]]
name = "extended_material_bindless"
path = "examples/shader/extended_material_bindless.rs"
doc-scrape-examples = true
[package.metadata.example.extended_material_bindless]
name = "Extended Bindless Material"
description = "Demonstrates bindless `ExtendedMaterial`"
category = "Shaders"
wasm = false
[[example]]
name = "cooldown"
path = "examples/usage/cooldown.rs"
doc-scrape-examples = true
[package.metadata.example.cooldown]
name = "Cooldown"
description = "Example for cooldown on button clicks"
category = "Usage"
wasm = true
[[example]]
name = "hotpatching_systems"
path = "examples/ecs/hotpatching_systems.rs"
doc-scrape-examples = true
required-features = ["hotpatching"]
[package.metadata.example.hotpatching_systems]
name = "Hotpatching Systems"
description = "Demonstrates how to hotpatch systems"
category = "ECS (Entity Component System)"
wasm = false
[[example]]
name = "core_widgets"
path = "examples/ui/core_widgets.rs"
doc-scrape-examples = true
[package.metadata.example.core_widgets]
name = "Core Widgets"
description = "Demonstrates use of core (headless) widgets in Bevy UI"
category = "UI (User Interface)"
wasm = true
[[example]]
name = "core_widgets_observers"
path = "examples/ui/core_widgets_observers.rs"
doc-scrape-examples = true
[package.metadata.example.core_widgets_observers]
name = "Core Widgets (w/Observers)"
description = "Demonstrates use of core (headless) widgets in Bevy UI, with Observers"
category = "UI (User Interface)"
wasm = true

View File

@ -1,4 +1,4 @@
# [![Bevy](assets/branding/bevy_logo_light_dark_and_dimmed.svg)](https://bevyengine.org)
# [![Bevy](assets/branding/bevy_logo_light_dark_and_dimmed.svg)](https://bevy.org)
[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/bevyengine/bevy#license)
[![Crates.io](https://img.shields.io/crates/v/bevy.svg)](https://crates.io/crates/bevy)
@ -13,7 +13,7 @@ Bevy is a refreshingly simple data-driven game engine built in Rust. It is free
## WARNING
Bevy is still in the early stages of development. Important features are missing. Documentation is sparse. A new version of Bevy containing breaking changes to the API is released [approximately once every 3 months](https://bevyengine.org/news/bevy-0-6/#the-train-release-schedule). We provide [migration guides](https://bevyengine.org/learn/migration-guides/), but we can't guarantee migrations will always be easy. Use only if you are willing to work in this environment.
Bevy is still in the early stages of development. Important features are missing. Documentation is sparse. A new version of Bevy containing breaking changes to the API is released [approximately once every 3 months](https://bevy.org/news/bevy-0-6/#the-train-release-schedule). We provide [migration guides](https://bevy.org/learn/migration-guides/), but we can't guarantee migrations will always be easy. Use only if you are willing to work in this environment.
**MSRV:** Bevy relies heavily on improvements in the Rust language and compiler.
As a result, the Minimum Supported Rust Version (MSRV) is generally close to "the latest stable release" of Rust.
@ -29,15 +29,15 @@ As a result, the Minimum Supported Rust Version (MSRV) is generally close to "th
## About
* **[Features](https://bevyengine.org):** A quick overview of Bevy's features.
* **[News](https://bevyengine.org/news/)**: A development blog that covers our progress, plans and shiny new features.
* **[Features](https://bevy.org):** A quick overview of Bevy's features.
* **[News](https://bevy.org/news/)**: A development blog that covers our progress, plans and shiny new features.
## Docs
* **[Quick Start Guide](https://bevyengine.org/learn/quick-start/introduction):** Bevy's official Quick Start Guide. The best place to start learning Bevy.
* **[Quick Start Guide](https://bevy.org/learn/quick-start/introduction):** Bevy's official Quick Start Guide. The best place to start learning Bevy.
* **[Bevy Rust API Docs](https://docs.rs/bevy):** Bevy's Rust API docs, which are automatically generated from the doc comments in this repo.
* **[Official Examples](https://github.com/bevyengine/bevy/tree/latest/examples):** Bevy's dedicated, runnable examples, which are great for digging into specific concepts.
* **[Community-Made Learning Resources](https://bevyengine.org/assets/#learning)**: More tutorials, documentation, and examples made by the Bevy community.
* **[Community-Made Learning Resources](https://bevy.org/assets/#learning)**: More tutorials, documentation, and examples made by the Bevy community.
## Community
@ -46,11 +46,11 @@ Before contributing or participating in discussions with the community, you shou
* **[Discord](https://discord.gg/bevy):** Bevy's official discord server.
* **[Reddit](https://reddit.com/r/bevy):** Bevy's official subreddit.
* **[GitHub Discussions](https://github.com/bevyengine/bevy/discussions):** The best place for questions about Bevy, answered right here!
* **[Bevy Assets](https://bevyengine.org/assets/):** A collection of awesome Bevy projects, tools, plugins and learning materials.
* **[Bevy Assets](https://bevy.org/assets/):** A collection of awesome Bevy projects, tools, plugins and learning materials.
### Contributing
If you'd like to help build Bevy, check out the **[Contributor's Guide](https://bevyengine.org/learn/contribute/introduction)**.
If you'd like to help build Bevy, check out the **[Contributor's Guide](https://bevy.org/learn/contribute/introduction)**.
For simple problems, feel free to [open an issue](https://github.com/bevyengine/bevy/issues) or
[PR](https://github.com/bevyengine/bevy/pulls) and tackle it yourself!
@ -58,9 +58,9 @@ For more complex architecture decisions and experimental mad science, please ope
## Getting Started
We recommend checking out the [Quick Start Guide](https://bevyengine.org/learn/quick-start/introduction) for a brief introduction.
We recommend checking out the [Quick Start Guide](https://bevy.org/learn/quick-start/introduction) for a brief introduction.
Follow the [Setup guide](https://bevyengine.org/learn/quick-start/getting-started/setup) to ensure your development environment is set up correctly.
Follow the [Setup guide](https://bevy.org/learn/quick-start/getting-started/setup) to ensure your development environment is set up correctly.
Once set up, you can quickly try out the [examples](https://github.com/bevyengine/bevy/tree/latest/examples) by cloning this repo and running the following commands:
```sh
@ -75,7 +75,7 @@ To draw a window with standard functionality enabled, use:
```rust
use bevy::prelude::*;
fn main(){
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.run();
@ -84,7 +84,7 @@ fn main(){
### Fast Compiles
Bevy can be built just fine using default configuration on stable Rust. However for really fast iterative compiles, you should enable the "fast compiles" setup by [following the instructions here](https://bevyengine.org/learn/quick-start/getting-started/setup).
Bevy can be built just fine using default configuration on stable Rust. However for really fast iterative compiles, you should enable the "fast compiles" setup by [following the instructions here](https://bevy.org/learn/quick-start/getting-started/setup).
## [Bevy Cargo Features][cargo_features]
@ -96,7 +96,7 @@ This [list][cargo_features] outlines the different cargo features supported by B
Bevy is the result of the hard work of many people. A huge thanks to all Bevy contributors, the many open source projects that have come before us, the [Rust gamedev ecosystem](https://arewegameyet.rs/), and the many libraries we build on.
A huge thanks to Bevy's [generous sponsors](https://bevyengine.org). Bevy will always be free and open source, but it isn't free to make. Please consider [sponsoring our work](https://bevyengine.org/donate/) if you like what we're building.
A huge thanks to Bevy's [generous sponsors](https://bevy.org). Bevy will always be free and open source, but it isn't free to make. Please consider [sponsoring our work](https://bevy.org/donate/) if you like what we're building.
<!-- This next line need to stay exactly as is. It is required for BrowserStack sponsorship. -->
This project is tested with BrowserStack.

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="svg2321"
version="1.1"
viewBox="0 0 63.304429 63.304432"
height="63.304432mm"
width="63.304428mm"
sodipodi:docname="bevy_solari.svg"
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
inkscape:zoom="0.27643554"
inkscape:cx="54.262198"
inkscape:cy="-311.10327"
inkscape:window-width="1440"
inkscape:window-height="788"
inkscape:window-x="-6"
inkscape:window-y="-6"
inkscape:window-maximized="1"
inkscape:current-layer="svg2321" />
<defs
id="defs2315" />
<metadata
id="metadata2318">
<rdf:RDF>
<cc:Work
rdf:about="" />
</rdf:RDF>
</metadata>
<g
id="g11"
style="display:inline"
transform="translate(-27.298342,-111.49082)">
<path
style="fill:#ff8904;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-opacity:1"
id="path10-7"
d="m 38.15031,109.44704 c 0.743942,3.07133 6.496307,7.00051 6.678099,10.15542 0.116231,2.01713 -5.225098,3.23914 -5.79502,5.17757 -0.891392,3.03182 2.125712,9.31077 0.705692,12.1339 -0.907907,1.80501 -6.144635,0.19263 -7.607418,1.5864 -2.287879,2.17994 -2.814466,9.12622 -5.455804,10.86111 -1.688772,1.10923 -5.417723,-2.9055 -7.381414,-2.42985 -3.071331,0.74394 -7.000511,6.49631 -10.1554234,6.6781 -2.0171309,0.11623 -3.2391354,-5.2251 -5.1775665,-5.79502 -3.03182154,-0.89139 -9.3107731,2.12571 -12.1339038,0.70569 -1.8050021,-0.9079 -0.1926248,-6.14463 -1.5863942,-7.60742 -2.1799381,-2.28787 -9.1262221,-2.81446 -10.8611151,-5.4558 -1.109224,-1.68877 2.9055,-5.41772 2.429851,-7.38141 -0.743942,-3.07133 -6.496306,-7.00051 -6.678099,-10.15543 -0.116231,-2.01713 5.225098,-3.23913 5.79502,-5.17756 0.891393,-3.03182 -2.125711,-9.31078 -0.705692,-12.13391 0.907907,-1.804999 6.144635,-0.19262 7.607418,-1.586391 2.2878791,-2.179939 2.8144662,-9.126222 5.4558046,-10.861115 1.6887711,-1.109225 5.4177222,2.905499 7.38141398,2.429851 3.07133072,-0.743942 7.00051032,-6.496307 10.15542342,-6.678099 2.01713,-0.116231 3.239135,5.225097 5.177566,5.79502 3.031822,0.891392 9.310773,-2.125712 12.133904,-0.705692 1.805002,0.907906 0.192625,6.144634 1.586394,7.607417 2.179938,2.28788 9.126222,2.814467 10.861115,5.455809 1.109224,1.68877 -2.905499,5.41772 -2.429851,7.38141 z"
transform="matrix(0.90823691,0,0,0.90823691,49.886257,35.27956)" />
<g
id="g3-6"
transform="translate(-517.96199,-278.01119)"
style="display:inline;opacity:1;fill:#ffd230;fill-opacity:1;stroke-width:0.755906;stroke-miterlimit:4;stroke-dasharray:none">
<g
transform="matrix(-0.40426719,-0.17438247,-0.17438247,0.40426719,678.77611,389.84765)"
style="fill:#ffd230;fill-opacity:1"
id="g2-1">
<path
id="path1-4"
style="fill:#ffd230;fill-opacity:1;stroke:none;stroke-width:0.559814px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 2246.0312,2340.1914 v 0 c -0.016,3e-4 -0.031,0 -0.047,0 -0.9804,3.0675 -1.7386,6.3997 -1.8828,10.1953 -0.2712,7.1263 0.453,11.4639 -0.3672,16.0801 -0.8202,4.6163 -3.2453,9.161 -9.4141,16.2871 -7.3424,8.482 -18.9789,15.0453 -32.4199,17.2637 -2.5015,1.5971 -5.1421,3.0609 -7.9199,4.3633 10.4618,3.9385 21.4025,4.1531 30.0761,1.3066 15.2793,-5.0141 14.0962,-8.6155 20.9434,-19.1074 2.1569,-3.3051 4.6474,-5.8282 7.1484,-7.9004 7.1248,3.1068 14.1431,5.1015 18.5157,4.6074 2.351,-5.4505 -0.057,-11.7712 -4.0586,-17.7461 3.2821,-10.196 -1.6986,-20.4059 -12.7305,-24.0156 -2.8775,-0.9415 -5.4633,-1.3844 -7.8438,-1.3379 z m 8.2754,14.9707 a 4.1668789,4.2454995 48.679502 0 1 3.1973,1.3965 4.1668789,4.2454995 48.679502 0 1 -0.4375,5.9336 4.1668789,4.2454995 48.679502 0 1 -5.9394,-0.3262 4.1668789,4.2454995 48.679502 0 1 0.4375,-5.9336 4.1668789,4.2454995 48.679502 0 1 2.7421,-1.0703 z m -68.375,45.3789 c 0.1273,0.075 0.2572,0.1408 0.3848,0.2149 0.131,-0.049 0.2642,-0.1009 0.3945,-0.1504 -0.2598,-0.023 -0.5188,-0.039 -0.7793,-0.064 z"
transform="matrix(-0.55180403,-0.23802315,-0.23802315,0.55180403,1946.7322,-620.612)" />
</g>
</g>
<g
id="g5-2"
transform="matrix(-0.45399624,0.36689705,0.36689705,0.45399624,73.527335,10.816805)"
style="display:inline;opacity:1;fill:#fef3c6;fill-opacity:1;stroke:none;stroke-width:0.755906;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
<g
id="g4-3"
transform="matrix(-0.35254083,0.28490586,0.28490586,0.35254083,477.11004,-1021.7666)"
style="opacity:1;fill:#fef3c6;fill-opacity:1">
<path
id="path2-2"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#fef3c6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.02362;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 2191.1465,2276.7832 c -5.9729,-0.035 -12.0979,2.348 -17.3613,7.459 -6.9129,6.7127 -9.0602,12.7555 -7.8477,20.2949 l 0.332,2.0684 -2.0664,-0.336 c -15.1877,-2.4609 -33.9847,-1.2178 -55.3711,7.4336 6.2868,2.6948 17.8259,7.1926 30.6309,13.3418 l 4.0605,1.9512 -4.414,0.8945 c -16.9087,3.4274 -36.9729,13.3275 -55.2989,34.9336 8.1981,-0.6372 24.9531,-2.6089 42.4278,-2.582 9.7138,0.015 19.2869,0.687 27.0859,2.709 7.7991,2.022 14.8874,6.6498 15.8861,10.0406 0.9987,3.3908 0.432,5.1761 -0.5519,7.8285 -0.9839,2.6524 -4.0098,6.6817 -8.1953,9.3418 -4.1855,2.6601 -9.4961,4.9849 -15.0137,6.9609 -11.0352,3.9521 -22.7798,6.4773 -27.9648,6.959 -1.1021,0.1024 -1.5421,0.4983 -1.9668,1.2696 -0.4247,0.7712 -0.659,1.9824 -0.6934,3.25 -0.046,1.6926 0.217,2.576 0.6949,3.246 0.4779,0.67 1.2243,0.9381 1.9934,0.9902 32.5822,2.2052 56.9441,-5.9907 74.6379,-13.0116 20.3508,-9.3311 33.2134,-27.7577 36.0058,-44.3477 1.7499,-10.395 1.3746,-15.4894 -0.3124,-19.8281 -1.6873,-4.3387 -4.9223,-8.1914 -9.0254,-15.5488 -2.6368,-4.7281 -4.1077,-9.367 -5.0196,-13.6875 l -0.1933,-0.9102 0.7265,-0.582 c 7.5403,-6.0446 13.6809,-12.6444 15.9102,-17.4492 -4.5742,-4.8648 -12.4787,-5.893 -21.3223,-4.9473 l -0.7265,0.076 -0.5118,-0.5215 c -4.7125,-4.8006 -10.5615,-7.2614 -16.5351,-7.2969 z m 2.6484,11.2324 c 2.593,-0.041 4.8808,1.7566 5.502,4.3223 0.7307,3.0216 -1.0812,6.0525 -4.0469,6.7695 -2.9656,0.7176 -5.9625,-1.1502 -6.6934,-4.1719 -0.7307,-3.0216 1.0812,-6.0525 4.0469,-6.7695 0.3902,-0.094 0.7897,-0.1445 1.1914,-0.1504 z"
transform="translate(5.0092774e-5,-757.87625)" />
</g>
</g>
<g
style="display:inline;opacity:1;fill:#fee685;fill-opacity:1;stroke-width:0.755906;stroke-miterlimit:4;stroke-dasharray:none"
id="g10-2"
transform="matrix(-0.50509374,0.06754889,0.06754889,0.50509374,156.75523,55.243465)">
<g
style="fill:#fee685;fill-opacity:1"
id="g9-1"
transform="translate(-20.244579,-6.1209206)">
<g
style="fill:#fee685;fill-opacity:1"
id="g8-6"
transform="translate(61.54776,-5.6726683)">
<g
id="g7-8"
style="fill:#fee685;fill-opacity:1">
<g
id="g6-5"
transform="matrix(-0.514626,0.06882369,0.06882369,0.514626,1184.3644,-811.9091)"
style="opacity:1;fill:#fee685;fill-opacity:1">
<path
id="path4-7"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#fee685;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.02362;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 2230.8945,2301.1738 c -1.9108,-0.039 -3.9117,0.162 -5.9785,0.6328 -0.1394,0.032 -0.2613,0.071 -0.3984,0.1036 -2.274,2.2481 -4.8127,4.5047 -7.5293,6.7168 0.8746,3.8597 2.1735,7.8829 4.4707,12.0019 3.9872,7.1495 7.2742,10.9657 9.2031,15.9258 1.9289,4.9601 2.2639,10.7945 0.4746,21.4238 -2.2183,13.178 -10.2404,27.1324 -22.959,37.4336 9.8717,-2.8792 18.2866,-8.1915 23.8575,-14.6269 6.0132,-6.9464 8.0191,-10.8762 8.7226,-14.836 0.7036,-3.9598 0.044,-8.2997 0.3242,-15.664 0.1805,-4.7447 1.1911,-8.8958 2.4766,-12.545 l 0.3086,-0.875 0.9219,-0.1211 c 8.2284,-1.0673 15.6654,-3.167 19.5097,-5.6484 -1.2349,-5.5522 -6.4807,-9.8603 -13.4277,-13.1348 l -0.6621,-0.3125 -0.166,-0.7129 c -2.2034,-9.4614 -9.5905,-15.5632 -19.1485,-15.7617 z m 4.7832,11.6856 a 4.8229105,4.9139092 17.729059 0 1 1.4473,0.2246 4.8229105,4.9139092 17.729059 0 1 3.0977,6.1484 4.8229105,4.9139092 17.729059 0 1 -6.0899,3.2129 4.8229105,4.9139092 17.729059 0 1 -3.0976,-6.1484 4.8229105,4.9139092 17.729059 0 1 4.6425,-3.4375 z"
transform="translate(1.2499985e-4,-757.87627)" />
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -5,7 +5,7 @@
),
},
entities: {
4294967296: (
4294967297: (
components: {
"bevy_ecs::name::Name": "joe",
"bevy_transform::components::global_transform::GlobalTransform": ((1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0)),
@ -23,7 +23,7 @@
),
},
),
4294967297: (
4294967298: (
components: {
"scene::ComponentA": (
x: 3.0,
@ -32,4 +32,4 @@
},
),
},
)
)

View File

@ -6,7 +6,8 @@ struct VertexOutput {
}
struct CustomMaterial {
time: f32,
// Needed for 16-bit alignment on WebGL2
time: vec4<f32>,
}
@group(2) @binding(0) var<uniform> material: CustomMaterial;
@ -15,5 +16,5 @@ struct CustomMaterial {
fn fragment(
mesh: VertexOutput,
) -> @location(0) vec4<f32> {
return make_polka_dots(mesh.uv, material.time);
}
return make_polka_dots(mesh.uv, material.time.x);
}

View File

@ -0,0 +1,107 @@
// The shader that goes with `extended_material_bindless.rs`.
//
// This code demonstrates how to write shaders that are compatible with both
// bindless and non-bindless mode. See the `#ifdef BINDLESS` blocks.
#import bevy_pbr::{
forward_io::{FragmentOutput, VertexOutput},
mesh_bindings::mesh,
pbr_fragment::pbr_input_from_standard_material,
pbr_functions::{apply_pbr_lighting, main_pass_post_lighting_processing},
}
#import bevy_render::bindless::{bindless_samplers_filtering, bindless_textures_2d}
#ifdef BINDLESS
#import bevy_pbr::pbr_bindings::{material_array, material_indices}
#else // BINDLESS
#import bevy_pbr::pbr_bindings::material
#endif // BINDLESS
// Stores the indices of the bindless resources in the bindless resource arrays,
// for the `ExampleBindlessExtension` fields.
struct ExampleBindlessExtendedMaterialIndices {
// The index of the `ExampleBindlessExtendedMaterial` data in
// `example_extended_material`.
material: u32,
// The index of the texture we're going to modulate the base color with in
// the `bindless_textures_2d` array.
modulate_texture: u32,
// The index of the sampler we're going to sample the modulated texture with
// in the `bindless_samplers_filtering` array.
modulate_texture_sampler: u32,
}
// Plain data associated with this example material.
struct ExampleBindlessExtendedMaterial {
// The color that we multiply the base color, base color texture, and
// modulated texture with.
modulate_color: vec4<f32>,
}
#ifdef BINDLESS
// The indices of the bindless resources in the bindless resource arrays, for
// the `ExampleBindlessExtension` fields.
@group(2) @binding(100) var<storage> example_extended_material_indices:
array<ExampleBindlessExtendedMaterialIndices>;
// An array that holds the `ExampleBindlessExtendedMaterial` plain old data,
// indexed by `ExampleBindlessExtendedMaterialIndices.material`.
@group(2) @binding(101) var<storage> example_extended_material:
array<ExampleBindlessExtendedMaterial>;
#else // BINDLESS
// In non-bindless mode, we simply use a uniform for the plain old data.
@group(2) @binding(50) var<uniform> example_extended_material: ExampleBindlessExtendedMaterial;
@group(2) @binding(51) var modulate_texture: texture_2d<f32>;
@group(2) @binding(52) var modulate_sampler: sampler;
#endif // BINDLESS
@fragment
fn fragment(
in: VertexOutput,
@builtin(front_facing) is_front: bool,
) -> FragmentOutput {
#ifdef BINDLESS
// Fetch the material slot. We'll use this in turn to fetch the bindless
// indices from `example_extended_material_indices`.
let slot = mesh[in.instance_index].material_and_lightmap_bind_group_slot & 0xffffu;
#endif // BINDLESS
// Generate a `PbrInput` struct from the `StandardMaterial` bindings.
var pbr_input = pbr_input_from_standard_material(in, is_front);
// Calculate the UV for the texture we're about to sample.
#ifdef BINDLESS
let uv_transform = material_array[material_indices[slot].material].uv_transform;
#else // BINDLESS
let uv_transform = material.uv_transform;
#endif // BINDLESS
let uv = (uv_transform * vec3(in.uv, 1.0)).xy;
// Multiply the base color by the `modulate_texture` and `modulate_color`.
#ifdef BINDLESS
// Notice how we fetch the texture, sampler, and plain extended material
// data from the appropriate arrays.
pbr_input.material.base_color *= textureSample(
bindless_textures_2d[example_extended_material_indices[slot].modulate_texture],
bindless_samplers_filtering[
example_extended_material_indices[slot].modulate_texture_sampler
],
uv
) * example_extended_material[example_extended_material_indices[slot].material].modulate_color;
#else // BINDLESS
pbr_input.material.base_color *= textureSample(modulate_texture, modulate_sampler, uv) *
example_extended_material.modulate_color;
#endif // BINDLESS
var out: FragmentOutput;
// Apply lighting.
out.color = apply_pbr_lighting(pbr_input);
// Apply in-shader post processing (fog, alpha-premultiply, and also
// tonemapping, debanding if the camera is non-HDR). Note this does not
// include fullscreen postprocessing effects like bloom.
out.color = main_pass_post_lighting_processing(pbr_input, out.color);
return out;
}

View File

@ -12,9 +12,9 @@ fn hash(value: u32) -> u32 {
var state = value;
state = state ^ 2747636419u;
state = state * 2654435769u;
state = state ^ state >> 16u;
state = state ^ (state >> 16u);
state = state * 2654435769u;
state = state ^ state >> 16u;
state = state ^ (state >> 16u);
state = state * 2654435769u;
return state;
}
@ -27,7 +27,7 @@ fn randomFloat(value: u32) -> f32 {
fn init(@builtin(global_invocation_id) invocation_id: vec3<u32>, @builtin(num_workgroups) num_workgroups: vec3<u32>) {
let location = vec2<i32>(i32(invocation_id.x), i32(invocation_id.y));
let randomNumber = randomFloat(invocation_id.y << 16u | invocation_id.x);
let randomNumber = randomFloat((invocation_id.y << 16u) | invocation_id.x);
let alive = randomNumber > 0.9;
let color = vec4<f32>(f32(alive));

View File

@ -40,5 +40,5 @@ fn make_polka_dots(pos: vec2<f32>, time: f32) -> vec4<f32> {
is_dot = step(dist_from_center, 0.3 + wave_normalized * 0.2);
}
return vec4<f32>(dot_color * is_dot, is_dot);
}
return vec4<f32>(dot_color * is_dot, 1.0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -24,7 +24,7 @@ bevy_reflect = { path = "../crates/bevy_reflect", features = ["functions"] }
bevy_render = { path = "../crates/bevy_render" }
bevy_tasks = { path = "../crates/bevy_tasks" }
bevy_utils = { path = "../crates/bevy_utils" }
bevy_platform_support = { path = "../crates/bevy_platform_support", default-features = false, features = [
bevy_platform = { path = "../crates/bevy_platform", default-features = false, features = [
"std",
] }
@ -32,6 +32,7 @@ bevy_platform_support = { path = "../crates/bevy_platform_support", default-feat
glam = "0.29"
rand = "0.8"
rand_chacha = "0.3"
nonmax = { version = "0.5", default-features = false }
# Make `bevy_render` compile on Linux with x11 windowing. x11 vs. Wayland does not matter here
# because the benches do not actually open any windows.

View File

@ -25,10 +25,10 @@ cargo bench -p benches -- name_fragment
cargo bench -p benches -- --list
# Save a baseline to be compared against later.
cargo bench -p benches --save-baseline before
cargo bench -p benches -- --save-baseline before
# Compare the current benchmarks against a baseline to find performance gains and regressions.
cargo bench -p benches --baseline before
cargo bench -p benches -- --baseline before
```
## Criterion

View File

@ -51,8 +51,7 @@ fn add_archetypes(world: &mut World, count: u16) {
pub fn no_archetypes(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("no_archetypes");
for i in 0..=5 {
let system_count = i * 20;
for system_count in [0, 10, 100] {
let (mut world, mut schedule) = setup(system_count);
group.bench_with_input(
BenchmarkId::new("system_count", system_count),
@ -69,7 +68,7 @@ pub fn no_archetypes(criterion: &mut Criterion) {
pub fn added_archetypes(criterion: &mut Criterion) {
const SYSTEM_COUNT: usize = 100;
let mut group = criterion.benchmark_group("added_archetypes");
for archetype_count in [100, 200, 500, 1000, 2000, 5000, 10000] {
for archetype_count in [100, 1_000, 10_000] {
group.bench_with_input(
BenchmarkId::new("archetype_count", archetype_count),
&archetype_count,

View File

@ -155,7 +155,7 @@ fn add_archetypes(world: &mut World, count: u16) {
fn empty_archetypes(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("empty_archetypes");
for archetype_count in [10, 100, 500, 1000, 2000, 5000, 10000] {
for archetype_count in [10, 100, 1_000, 10_000] {
let (mut world, mut schedule) = setup(true, |schedule| {
schedule.add_systems(iter);
});
@ -186,7 +186,7 @@ fn empty_archetypes(criterion: &mut Criterion) {
},
);
}
for archetype_count in [10, 100, 500, 1000, 2000, 5000, 10000] {
for archetype_count in [10, 100, 1_000, 10_000] {
let (mut world, mut schedule) = setup(true, |schedule| {
schedule.add_systems(for_each);
});
@ -217,7 +217,7 @@ fn empty_archetypes(criterion: &mut Criterion) {
},
);
}
for archetype_count in [10, 100, 500, 1000, 2000, 5000, 10000] {
for archetype_count in [10, 100, 1_000, 10_000] {
let (mut world, mut schedule) = setup(true, |schedule| {
schedule.add_systems(par_for_each);
});

View File

@ -155,7 +155,7 @@ fn bench_clone_hierarchy<B: Bundle + Default + GetTypeRegistration>(
for parent in current_hierarchy_level {
for _ in 0..children {
let child_id = world.spawn((B::default(), ChildOf { parent })).id();
let child_id = world.spawn((B::default(), ChildOf(parent))).id();
hierarchy_level.push(child_id);
}
}

View File

@ -1,6 +1,6 @@
use bevy_ecs::prelude::*;
#[derive(Event)]
#[derive(Event, BufferedEvent)]
struct BenchEvent<const SIZE: usize>([u8; SIZE]);
pub struct Benchmark<const SIZE: usize>(Events<BenchEvent<SIZE>>);

View File

@ -9,19 +9,19 @@ fn send(c: &mut Criterion) {
let mut group = c.benchmark_group("events_send");
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(4));
for count in [100, 1000, 10000, 50000] {
for count in [100, 1_000, 10_000] {
group.bench_function(format!("size_4_events_{}", count), |b| {
let mut bench = send::Benchmark::<4>::new(count);
b.iter(move || bench.run());
});
}
for count in [100, 1000, 10000, 50000] {
for count in [100, 1_000, 10_000] {
group.bench_function(format!("size_16_events_{}", count), |b| {
let mut bench = send::Benchmark::<16>::new(count);
b.iter(move || bench.run());
});
}
for count in [100, 1000, 10000, 50000] {
for count in [100, 1_000, 10_000] {
group.bench_function(format!("size_512_events_{}", count), |b| {
let mut bench = send::Benchmark::<512>::new(count);
b.iter(move || bench.run());
@ -34,19 +34,19 @@ fn iter(c: &mut Criterion) {
let mut group = c.benchmark_group("events_iter");
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(4));
for count in [100, 1000, 10000, 50000] {
for count in [100, 1_000, 10_000] {
group.bench_function(format!("size_4_events_{}", count), |b| {
let mut bench = iter::Benchmark::<4>::new(count);
b.iter(move || bench.run());
});
}
for count in [100, 1000, 10000, 50000] {
for count in [100, 1_000, 10_000] {
group.bench_function(format!("size_16_events_{}", count), |b| {
let mut bench = iter::Benchmark::<4>::new(count);
b.iter(move || bench.run());
});
}
for count in [100, 1000, 10000, 50000] {
for count in [100, 1_000, 10_000] {
group.bench_function(format!("size_512_events_{}", count), |b| {
let mut bench = iter::Benchmark::<512>::new(count);
b.iter(move || bench.run());

View File

@ -1,6 +1,6 @@
use bevy_ecs::prelude::*;
#[derive(Event)]
#[derive(Event, BufferedEvent)]
struct BenchEvent<const SIZE: usize>([u8; SIZE]);
impl<const SIZE: usize> Default for BenchEvent<SIZE> {

View File

@ -45,7 +45,6 @@ pub fn heavy_compute(c: &mut Criterion) {
let mut system = IntoSystem::into_system(sys);
system.initialize(&mut world);
system.update_archetype_component_access(world.as_unsafe_world_cell());
b.iter(move || system.run((), &mut world));
});

View File

@ -37,7 +37,6 @@ impl Benchmark {
let mut system = IntoSystem::into_system(query_system);
system.initialize(&mut world);
system.update_archetype_component_access(world.as_unsafe_world_cell());
Self(world, Box::new(system))
}

View File

@ -61,14 +61,10 @@ pub fn event_propagation(criterion: &mut Criterion) {
group.finish();
}
#[derive(Clone, Component)]
#[derive(Event, EntityEvent, Clone, Component)]
#[entity_event(traversal = &'static ChildOf, auto_propagate)]
struct TestEvent<const N: usize> {}
impl<const N: usize> Event for TestEvent<N> {
type Traversal = &'static ChildOf;
const AUTO_PROPAGATE: bool = true;
}
fn send_events<const N: usize, const N_EVENTS: usize>(world: &mut World, leaves: &[Entity]) {
let target = leaves.iter().choose(&mut rand::thread_rng()).unwrap();
@ -117,6 +113,6 @@ fn add_listeners_to_hierarchy<const DENSITY: usize, const N: usize>(
}
}
fn empty_listener<const N: usize>(trigger: Trigger<TestEvent<N>>) {
fn empty_listener<const N: usize>(trigger: On<TestEvent<N>>) {
black_box(trigger);
}

View File

@ -1,8 +1,8 @@
use core::hint::black_box;
use bevy_ecs::{
event::Event,
observer::{Trigger, TriggerTargets},
event::{EntityEvent, Event},
observer::{On, TriggerTargets},
world::World,
};
@ -13,7 +13,7 @@ fn deterministic_rand() -> ChaCha8Rng {
ChaCha8Rng::seed_from_u64(42)
}
#[derive(Clone, Event)]
#[derive(Clone, Event, EntityEvent)]
struct EventBase;
pub fn observe_simple(criterion: &mut Criterion) {
@ -46,7 +46,7 @@ pub fn observe_simple(criterion: &mut Criterion) {
group.finish();
}
fn empty_listener_base(trigger: Trigger<EventBase>) {
fn empty_listener_base(trigger: On<EventBase>) {
black_box(trigger);
}

View File

@ -17,15 +17,14 @@ pub fn run_condition_yes(criterion: &mut Criterion) {
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(3));
fn empty() {}
for amount in 0..21 {
for amount in [10, 100, 1_000] {
let mut schedule = Schedule::default();
schedule.add_systems(empty.run_if(yes));
for _ in 0..amount {
for _ in 0..(amount / 5) {
schedule.add_systems((empty, empty, empty, empty, empty).distributive_run_if(yes));
}
// run once to initialize systems
schedule.run(&mut world);
group.bench_function(format!("{:03}_systems", 5 * amount + 1), |bencher| {
group.bench_function(format!("{}_systems", amount), |bencher| {
bencher.iter(|| {
schedule.run(&mut world);
});
@ -40,15 +39,14 @@ pub fn run_condition_no(criterion: &mut Criterion) {
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(3));
fn empty() {}
for amount in 0..21 {
for amount in [10, 100, 1_000] {
let mut schedule = Schedule::default();
schedule.add_systems(empty.run_if(no));
for _ in 0..amount {
for _ in 0..(amount / 5) {
schedule.add_systems((empty, empty, empty, empty, empty).distributive_run_if(no));
}
// run once to initialize systems
schedule.run(&mut world);
group.bench_function(format!("{:03}_systems", 5 * amount + 1), |bencher| {
group.bench_function(format!("{}_systems", amount), |bencher| {
bencher.iter(|| {
schedule.run(&mut world);
});
@ -70,17 +68,16 @@ pub fn run_condition_yes_with_query(criterion: &mut Criterion) {
fn yes_with_query(query: Single<&TestBool>) -> bool {
query.0
}
for amount in 0..21 {
for amount in [10, 100, 1_000] {
let mut schedule = Schedule::default();
schedule.add_systems(empty.run_if(yes_with_query));
for _ in 0..amount {
for _ in 0..(amount / 5) {
schedule.add_systems(
(empty, empty, empty, empty, empty).distributive_run_if(yes_with_query),
);
}
// run once to initialize systems
schedule.run(&mut world);
group.bench_function(format!("{:03}_systems", 5 * amount + 1), |bencher| {
group.bench_function(format!("{}_systems", amount), |bencher| {
bencher.iter(|| {
schedule.run(&mut world);
});
@ -99,17 +96,16 @@ pub fn run_condition_yes_with_resource(criterion: &mut Criterion) {
fn yes_with_resource(res: Res<TestBool>) -> bool {
res.0
}
for amount in 0..21 {
for amount in [10, 100, 1_000] {
let mut schedule = Schedule::default();
schedule.add_systems(empty.run_if(yes_with_resource));
for _ in 0..amount {
for _ in 0..(amount / 5) {
schedule.add_systems(
(empty, empty, empty, empty, empty).distributive_run_if(yes_with_resource),
);
}
// run once to initialize systems
schedule.run(&mut world);
group.bench_function(format!("{:03}_systems", 5 * amount + 1), |bencher| {
group.bench_function(format!("{}_systems", amount), |bencher| {
bencher.iter(|| {
schedule.run(&mut world);
});

View File

@ -20,25 +20,25 @@ pub fn empty_systems(criterion: &mut Criterion) {
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(3));
fn empty() {}
for amount in 0..5 {
for amount in [0, 2, 4] {
let mut schedule = Schedule::default();
for _ in 0..amount {
schedule.add_systems(empty);
}
schedule.run(&mut world);
group.bench_function(format!("{:03}_systems", amount), |bencher| {
group.bench_function(format!("{}_systems", amount), |bencher| {
bencher.iter(|| {
schedule.run(&mut world);
});
});
}
for amount in 1..21 {
for amount in [10, 100, 1_000] {
let mut schedule = Schedule::default();
for _ in 0..amount {
for _ in 0..(amount / 5) {
schedule.add_systems((empty, empty, empty, empty, empty));
}
schedule.run(&mut world);
group.bench_function(format!("{:03}_systems", 5 * amount), |bencher| {
group.bench_function(format!("{}_systems", amount), |bencher| {
bencher.iter(|| {
schedule.run(&mut world);
});
@ -67,23 +67,21 @@ pub fn busy_systems(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("busy_systems");
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(3));
for entity_bunches in 1..6 {
for entity_bunches in [1, 3, 5] {
world.spawn_batch((0..4 * ENTITY_BUNCH).map(|_| (A(0.0), B(0.0))));
world.spawn_batch((0..4 * ENTITY_BUNCH).map(|_| (A(0.0), B(0.0), C(0.0))));
world.spawn_batch((0..ENTITY_BUNCH).map(|_| (A(0.0), B(0.0), C(0.0), D(0.0))));
world.spawn_batch((0..ENTITY_BUNCH).map(|_| (A(0.0), B(0.0), C(0.0), E(0.0))));
for system_amount in 0..5 {
for system_amount in [3, 9, 15] {
let mut schedule = Schedule::default();
schedule.add_systems((ab, cd, ce));
for _ in 0..system_amount {
for _ in 0..(system_amount / 3) {
schedule.add_systems((ab, cd, ce));
}
schedule.run(&mut world);
group.bench_function(
format!(
"{:02}x_entities_{:02}_systems",
entity_bunches,
3 * system_amount + 3
entity_bunches, system_amount
),
|bencher| {
bencher.iter(|| {
@ -119,22 +117,20 @@ pub fn contrived(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("contrived");
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(3));
for entity_bunches in 1..6 {
for entity_bunches in [1, 3, 5] {
world.spawn_batch((0..ENTITY_BUNCH).map(|_| (A(0.0), B(0.0), C(0.0), D(0.0))));
world.spawn_batch((0..ENTITY_BUNCH).map(|_| (A(0.0), B(0.0))));
world.spawn_batch((0..ENTITY_BUNCH).map(|_| (C(0.0), D(0.0))));
for system_amount in 0..5 {
for system_amount in [3, 9, 15] {
let mut schedule = Schedule::default();
schedule.add_systems((s_0, s_1, s_2));
for _ in 0..system_amount {
for _ in 0..(system_amount / 3) {
schedule.add_systems((s_0, s_1, s_2));
}
schedule.run(&mut world);
group.bench_function(
format!(
"{:02}x_entities_{:02}_systems",
entity_bunches,
3 * system_amount + 3
entity_bunches, system_amount
),
|bencher| {
bencher.iter(|| {

View File

@ -137,6 +137,7 @@ pub fn empty_schedule_run(criterion: &mut Criterion) {
});
let mut schedule = Schedule::default();
#[expect(deprecated, reason = "We still need to test/bench this.")]
schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::Simple);
group.bench_function("Simple", |bencher| {
bencher.iter(|| schedule.run(app.world_mut()));

View File

@ -36,7 +36,7 @@ pub fn spawn_commands(criterion: &mut Criterion) {
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(4));
for entity_count in (1..5).map(|i| i * 2 * 1000) {
for entity_count in [100, 1_000, 10_000] {
group.bench_function(format!("{}_entities", entity_count), |bencher| {
let mut world = World::default();
let mut command_queue = CommandQueue::default();
@ -62,6 +62,31 @@ pub fn spawn_commands(criterion: &mut Criterion) {
group.finish();
}
pub fn nonempty_spawn_commands(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("nonempty_spawn_commands");
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(4));
for entity_count in [100, 1_000, 10_000] {
group.bench_function(format!("{}_entities", entity_count), |bencher| {
let mut world = World::default();
let mut command_queue = CommandQueue::default();
bencher.iter(|| {
let mut commands = Commands::new(&mut command_queue, &world);
for i in 0..entity_count {
if black_box(i % 2 == 0) {
commands.spawn(A);
}
}
command_queue.apply(&mut world);
});
});
}
group.finish();
}
#[derive(Default, Component)]
struct Matrix([[f32; 4]; 4]);
@ -92,28 +117,6 @@ pub fn insert_commands(criterion: &mut Criterion) {
command_queue.apply(&mut world);
});
});
group.bench_function("insert_or_spawn_batch", |bencher| {
let mut world = World::default();
let mut command_queue = CommandQueue::default();
let mut entities = Vec::new();
for _ in 0..entity_count {
entities.push(world.spawn_empty().id());
}
bencher.iter(|| {
let mut commands = Commands::new(&mut command_queue, &world);
let mut values = Vec::with_capacity(entity_count);
for entity in &entities {
values.push((*entity, (Matrix::default(), Vec3::default())));
}
#[expect(
deprecated,
reason = "This needs to be supported for now, and therefore still needs the benchmark."
)]
commands.insert_or_spawn_batch(values);
command_queue.apply(&mut world);
});
});
group.bench_function("insert_batch", |bencher| {
let mut world = World::default();
let mut command_queue = CommandQueue::default();
@ -158,7 +161,7 @@ pub fn fake_commands(criterion: &mut Criterion) {
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(4));
for command_count in (1..5).map(|i| i * 2 * 1000) {
for command_count in [100, 1_000, 10_000] {
group.bench_function(format!("{}_commands", command_count), |bencher| {
let mut world = World::default();
let mut command_queue = CommandQueue::default();
@ -203,7 +206,7 @@ pub fn sized_commands_impl<T: Default + Command>(criterion: &mut Criterion) {
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(4));
for command_count in (1..5).map(|i| i * 2 * 1000) {
for command_count in [100, 1_000, 10_000] {
group.bench_function(format!("{}_commands", command_count), |bencher| {
let mut world = World::default();
let mut command_queue = CommandQueue::default();

View File

@ -1,5 +1,5 @@
use bevy_ecs::prelude::*;
use criterion::Criterion;
use criterion::{BatchSize, Criterion};
use glam::*;
#[derive(Component)]
@ -12,19 +12,24 @@ pub fn world_despawn(criterion: &mut Criterion) {
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(4));
for entity_count in (0..5).map(|i| 10_u32.pow(i)) {
let mut world = World::default();
for _ in 0..entity_count {
world.spawn((A(Mat4::default()), B(Vec4::default())));
}
let ents = world.iter_entities().map(|e| e.id()).collect::<Vec<_>>();
for entity_count in [1, 100, 10_000] {
group.bench_function(format!("{}_entities", entity_count), |bencher| {
bencher.iter(|| {
ents.iter().for_each(|e| {
world.despawn(*e);
});
});
bencher.iter_batched_ref(
|| {
let mut world = World::default();
for _ in 0..entity_count {
world.spawn((A(Mat4::default()), B(Vec4::default())));
}
let ents = world.iter_entities().map(|e| e.id()).collect::<Vec<_>>();
(world, ents)
},
|(world, ents)| {
ents.iter().for_each(|e| {
world.despawn(*e);
});
},
BatchSize::SmallInput,
);
});
}

View File

@ -1,5 +1,5 @@
use bevy_ecs::prelude::*;
use criterion::Criterion;
use criterion::{BatchSize, Criterion};
use glam::*;
#[derive(Component)]
@ -12,23 +12,31 @@ pub fn world_despawn_recursive(criterion: &mut Criterion) {
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(4));
for entity_count in (0..5).map(|i| 10_u32.pow(i)) {
let mut world = World::default();
for _ in 0..entity_count {
world
.spawn((A(Mat4::default()), B(Vec4::default())))
.with_children(|parent| {
parent.spawn((A(Mat4::default()), B(Vec4::default())));
});
}
let ents = world.iter_entities().map(|e| e.id()).collect::<Vec<_>>();
for entity_count in [1, 100, 10_000] {
group.bench_function(format!("{}_entities", entity_count), |bencher| {
bencher.iter(|| {
ents.iter().for_each(|e| {
world.entity_mut(*e).despawn();
});
});
bencher.iter_batched_ref(
|| {
let mut world = World::default();
let parent_ents = (0..entity_count)
.map(|_| {
world
.spawn((A(Mat4::default()), B(Vec4::default())))
.with_children(|parent| {
parent.spawn((A(Mat4::default()), B(Vec4::default())));
})
.id()
})
.collect::<Vec<_>>();
(world, parent_ents)
},
|(world, parent_ents)| {
parent_ents.iter().for_each(|e| {
world.despawn(*e);
});
},
BatchSize::SmallInput,
);
});
}

View File

@ -1,4 +1,4 @@
use bevy_ecs::entity::{hash_set::EntityHashSet, Entity};
use bevy_ecs::entity::{Entity, EntityGeneration, EntityHashSet};
use criterion::{BenchmarkId, Criterion, Throughput};
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;
@ -17,10 +17,14 @@ fn make_entity(rng: &mut impl Rng, size: usize) -> Entity {
let generation = 1.0 + -(1.0 - x).log2() * 2.0;
// this is not reliable, but we're internal so a hack is ok
let bits = ((generation as u64) << 32) | (id as u64);
let id = id as u64 + 1;
let bits = ((generation as u64) << 32) | id;
let e = Entity::from_bits(bits);
assert_eq!(e.index(), id as u32);
assert_eq!(e.generation(), generation as u32);
assert_eq!(e.index(), !(id as u32));
assert_eq!(
e.generation(),
EntityGeneration::FIRST.after_versions(generation as u32)
);
e
}

View File

@ -17,6 +17,7 @@ criterion_group!(
benches,
empty_commands,
spawn_commands,
nonempty_spawn_commands,
insert_commands,
fake_commands,
zero_sized_commands,

View File

@ -12,7 +12,7 @@ pub fn world_spawn(criterion: &mut Criterion) {
group.warm_up_time(core::time::Duration::from_millis(500));
group.measurement_time(core::time::Duration::from_secs(4));
for entity_count in (0..5).map(|i| 10_u32.pow(i)) {
for entity_count in [1, 100, 10_000] {
group.bench_function(format!("{}_entities", entity_count), |bencher| {
let mut world = World::default();
bencher.iter(|| {

View File

@ -1,9 +1,10 @@
use core::hint::black_box;
use nonmax::NonMaxU32;
use bevy_ecs::{
bundle::{Bundle, NoBundleEffect},
component::Component,
entity::Entity,
entity::{Entity, EntityRow},
system::{Query, SystemState},
world::World,
};
@ -53,7 +54,9 @@ pub fn world_entity(criterion: &mut Criterion) {
bencher.iter(|| {
for i in 0..entity_count {
let entity = Entity::from_raw(i);
let entity =
// SAFETY: Range is exclusive.
Entity::from_raw(EntityRow::new(unsafe { NonMaxU32::new_unchecked(i) }));
black_box(world.entity(entity));
}
});
@ -74,7 +77,9 @@ pub fn world_get(criterion: &mut Criterion) {
bencher.iter(|| {
for i in 0..entity_count {
let entity = Entity::from_raw(i);
let entity =
// SAFETY: Range is exclusive.
Entity::from_raw(EntityRow::new(unsafe { NonMaxU32::new_unchecked(i) }));
assert!(world.get::<Table>(entity).is_some());
}
});
@ -84,7 +89,9 @@ pub fn world_get(criterion: &mut Criterion) {
bencher.iter(|| {
for i in 0..entity_count {
let entity = Entity::from_raw(i);
let entity =
// SAFETY: Range is exclusive.
Entity::from_raw(EntityRow::new(unsafe { NonMaxU32::new_unchecked(i) }));
assert!(world.get::<Sparse>(entity).is_some());
}
});
@ -106,7 +113,9 @@ pub fn world_query_get(criterion: &mut Criterion) {
bencher.iter(|| {
for i in 0..entity_count {
let entity = Entity::from_raw(i);
let entity =
// SAFETY: Range is exclusive.
Entity::from_raw(EntityRow::new(unsafe { NonMaxU32::new_unchecked(i) }));
assert!(query.get(&world, entity).is_ok());
}
});
@ -131,7 +140,9 @@ pub fn world_query_get(criterion: &mut Criterion) {
bencher.iter(|| {
for i in 0..entity_count {
let entity = Entity::from_raw(i);
let entity =
// SAFETY: Range is exclusive.
Entity::from_raw(EntityRow::new(unsafe { NonMaxU32::new_unchecked(i) }));
assert!(query.get(&world, entity).is_ok());
}
});
@ -142,7 +153,9 @@ pub fn world_query_get(criterion: &mut Criterion) {
bencher.iter(|| {
for i in 0..entity_count {
let entity = Entity::from_raw(i);
let entity =
// SAFETY: Range is exclusive.
Entity::from_raw(EntityRow::new(unsafe { NonMaxU32::new_unchecked(i) }));
assert!(query.get(&world, entity).is_ok());
}
});
@ -169,7 +182,10 @@ pub fn world_query_get(criterion: &mut Criterion) {
bencher.iter(|| {
for i in 0..entity_count {
let entity = Entity::from_raw(i);
// SAFETY: Range is exclusive.
let entity = Entity::from_raw(EntityRow::new(unsafe {
NonMaxU32::new_unchecked(i)
}));
assert!(query.get(&world, entity).is_ok());
}
});

View File

@ -32,7 +32,7 @@ fn segment_ease(c: &mut Criterion) {
fn curve_position(c: &mut Criterion) {
/// A helper function that benchmarks calling [`CubicCurve::position()`] over a generic [`VectorSpace`].
fn bench_curve<M: Measurement, P: VectorSpace>(
fn bench_curve<M: Measurement, P: VectorSpace<Scalar = f32>>(
group: &mut BenchmarkGroup<M>,
name: &str,
curve: CubicCurve<P>,

View File

@ -37,7 +37,7 @@ fn create_mesh(vertices_per_side: u32) -> SimpleMesh {
for p in 0..vertices_per_side.pow(2) {
let (x, z) = p_to_xz_norm(p, vertices_per_side);
// Push a new vertice to the mesh. We translate all vertices so the final square is
// Push a new vertex to the mesh. We translate all vertices so the final square is
// centered at (0, 0), instead of (0.5, 0.5).
positions.push([x - 0.5, 0.0, z - 0.5]);

View File

@ -1,7 +1,7 @@
use core::{fmt::Write, hint::black_box, iter, time::Duration};
use benches::bench;
use bevy_platform_support::collections::HashMap;
use bevy_platform::collections::HashMap;
use bevy_reflect::{DynamicMap, Map};
use criterion::{
criterion_group, measurement::Measurement, AxisScale, BatchSize, BenchmarkGroup, BenchmarkId,

View File

@ -0,0 +1,96 @@
use core::hint::black_box;
use criterion::{criterion_group, Criterion};
use rand::random;
use std::time::{Duration, Instant};
use bevy_render::{
mesh::{Indices, Mesh, PrimitiveTopology},
render_asset::RenderAssetUsages,
};
const GRID_SIZE: usize = 256;
fn compute_normals(c: &mut Criterion) {
let indices = Indices::U32(
(0..GRID_SIZE - 1)
.flat_map(|i| std::iter::repeat(i).zip(0..GRID_SIZE - 1))
.flat_map(|(i, j)| {
let tl = ((GRID_SIZE * j) + i) as u32;
let tr = tl + 1;
let bl = ((GRID_SIZE * (j + 1)) + i) as u32;
let br = bl + 1;
[tl, bl, tr, tr, bl, br]
})
.collect(),
);
let new_mesh = || {
let positions = (0..GRID_SIZE)
.flat_map(|i| std::iter::repeat(i).zip(0..GRID_SIZE))
.map(|(i, j)| [i as f32, j as f32, random::<f32>()])
.collect::<Vec<_>>();
Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::MAIN_WORLD,
)
.with_inserted_attribute(Mesh::ATTRIBUTE_POSITION, positions)
.with_inserted_indices(indices.clone())
};
c.bench_function("smooth_normals", |b| {
b.iter_custom(|iters| {
let mut total = Duration::default();
for _ in 0..iters {
let mut mesh = new_mesh();
black_box(mesh.attribute(Mesh::ATTRIBUTE_NORMAL));
let start = Instant::now();
mesh.compute_smooth_normals();
let end = Instant::now();
black_box(mesh.attribute(Mesh::ATTRIBUTE_NORMAL));
total += end.duration_since(start);
}
total
});
});
c.bench_function("face_weighted_normals", |b| {
b.iter_custom(|iters| {
let mut total = Duration::default();
for _ in 0..iters {
let mut mesh = new_mesh();
black_box(mesh.attribute(Mesh::ATTRIBUTE_NORMAL));
let start = Instant::now();
mesh.compute_smooth_normals();
let end = Instant::now();
black_box(mesh.attribute(Mesh::ATTRIBUTE_NORMAL));
total += end.duration_since(start);
}
total
});
});
let new_mesh = || {
new_mesh()
.with_duplicated_vertices()
.with_computed_flat_normals()
};
c.bench_function("flat_normals", |b| {
b.iter_custom(|iters| {
let mut total = Duration::default();
for _ in 0..iters {
let mut mesh = new_mesh();
black_box(mesh.attribute(Mesh::ATTRIBUTE_NORMAL));
let start = Instant::now();
mesh.compute_flat_normals();
let end = Instant::now();
black_box(mesh.attribute(Mesh::ATTRIBUTE_NORMAL));
total += end.duration_since(start);
}
total
});
});
}
criterion_group!(benches, compute_normals);

View File

@ -1,6 +1,11 @@
use criterion::criterion_main;
mod compute_normals;
mod render_layers;
mod torus;
criterion_main!(render_layers::benches, torus::benches);
criterion_main!(
render_layers::benches,
compute_normals::benches,
torus::benches
);

View File

@ -41,7 +41,6 @@ disallowed-methods = [
{ path = "f32::asinh", reason = "use bevy_math::ops::asinh instead for libm determinism" },
{ path = "f32::acosh", reason = "use bevy_math::ops::acosh instead for libm determinism" },
{ path = "f32::atanh", reason = "use bevy_math::ops::atanh instead for libm determinism" },
{ path = "criterion::black_box", reason = "use core::hint::black_box instead" },
]
# Require `bevy_ecs::children!` to use `[]` braces, instead of `()` or `{}`.

View File

@ -1,9 +1,9 @@
[package]
name = "bevy_a11y"
version = "0.16.0-dev"
version = "0.17.0-dev"
edition = "2024"
description = "Provides accessibility support for Bevy Engine"
homepage = "https://bevyengine.org"
homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy", "accessibility", "a11y"]
@ -18,28 +18,17 @@ bevy_reflect = [
"dep:bevy_reflect",
"bevy_app/bevy_reflect",
"bevy_ecs/bevy_reflect",
"bevy_input_focus/bevy_reflect",
]
## Adds serialization support through `serde`.
serialize = [
"dep:serde",
"bevy_ecs/serialize",
"bevy_input_focus/serialize",
"accesskit/serde",
]
serialize = ["dep:serde", "bevy_ecs/serialize", "accesskit/serde"]
# Platform Compatibility
## Allows access to the `std` crate. Enabling this feature will prevent compilation
## on `no_std` targets, but provides access to certain additional features on
## supported platforms.
std = [
"bevy_app/std",
"bevy_ecs/std",
"bevy_reflect/std",
"bevy_input_focus/std",
]
std = ["bevy_app/std", "bevy_ecs/std", "bevy_reflect/std"]
## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`.
@ -47,22 +36,17 @@ critical-section = [
"bevy_app/critical-section",
"bevy_ecs/critical-section",
"bevy_reflect?/critical-section",
"bevy_input_focus/critical-section",
]
## Uses the `libm` maths library instead of the one provided in `std` and `core`.
libm = ["bevy_input_focus/libm"]
[dependencies]
# bevy
bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false }
bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", default-features = false }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", default-features = false, optional = true }
bevy_input_focus = { path = "../bevy_input_focus", version = "0.16.0-dev", default-features = false }
bevy_app = { path = "../bevy_app", version = "0.17.0-dev", default-features = false }
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev", default-features = false }
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", default-features = false, optional = true }
# other
accesskit = { version = "0.17", default-features = false }
accesskit = { version = "0.19", default-features = false }
serde = { version = "1", default-features = false, features = [
"alloc",
], optional = true }

View File

@ -1,8 +1,8 @@
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
html_logo_url = "https://bevy.org/assets/icon.png",
html_favicon_url = "https://bevy.org/assets/icon.png"
)]
#![no_std]
@ -26,7 +26,8 @@ use accesskit::Node;
use bevy_app::Plugin;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
prelude::{Component, Event},
component::Component,
event::{BufferedEvent, Event},
resource::Resource,
schedule::SystemSet,
};
@ -44,7 +45,7 @@ use serde::{Deserialize, Serialize};
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
/// Wrapper struct for [`accesskit::ActionRequest`]. Required to allow it to be used as an `Event`.
#[derive(Event, Deref, DerefMut)]
#[derive(Event, BufferedEvent, Deref, DerefMut)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct ActionRequest(pub accesskit::ActionRequest);
@ -137,11 +138,15 @@ impl From<Node> for AccessibilityNode {
all(feature = "bevy_reflect", feature = "serialize"),
reflect(Serialize, Deserialize, Clone)
)]
pub enum AccessibilitySystem {
pub enum AccessibilitySystems {
/// Update the accessibility tree
Update,
}
/// Deprecated alias for [`AccessibilitySystems`].
#[deprecated(since = "0.17.0", note = "Renamed to `AccessibilitySystems`.")]
pub type AccessibilitySystem = AccessibilitySystems;
/// Plugin managing non-GUI aspects of integrating with accessibility APIs.
#[derive(Default)]
pub struct AccessibilityPlugin;

View File

@ -1,37 +1,38 @@
[package]
name = "bevy_animation"
version = "0.16.0-dev"
version = "0.17.0-dev"
edition = "2024"
description = "Provides animation functionality for Bevy Engine"
homepage = "https://bevyengine.org"
homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[dependencies]
# bevy
bevy_app = { path = "../bevy_app", version = "0.16.0-dev" }
bevy_asset = { path = "../bevy_asset", version = "0.16.0-dev" }
bevy_color = { path = "../bevy_color", version = "0.16.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" }
bevy_log = { path = "../bevy_log", version = "0.16.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.16.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [
bevy_app = { path = "../bevy_app", version = "0.17.0-dev" }
bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
bevy_color = { path = "../bevy_color", version = "0.17.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
bevy_log = { path = "../bevy_log", version = "0.17.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
bevy_mesh = { path = "../bevy_mesh", version = "0.17.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", features = [
"petgraph",
] }
bevy_render = { path = "../bevy_render", version = "0.16.0-dev" }
bevy_time = { path = "../bevy_time", version = "0.16.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.16.0-dev" }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
bevy_time = { path = "../bevy_time", version = "0.17.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
"std",
"serialize",
] }
# other
petgraph = { version = "0.7", features = ["serde-1"] }
ron = "0.8"
ron = "0.10"
serde = "1"
blake3 = { version = "1.0" }
downcast-rs = { version = "2", default-features = false, features = ["std"] }

View File

@ -100,43 +100,48 @@ use bevy_math::curve::{
iterable::IterableCurve,
Curve, Interval,
};
use bevy_platform_support::hash::Hashed;
use bevy_mesh::morph::MorphWeights;
use bevy_platform::hash::Hashed;
use bevy_reflect::{FromReflect, Reflect, Reflectable, TypeInfo, Typed};
use bevy_render::mesh::morph::MorphWeights;
use downcast_rs::{impl_downcast, Downcast};
/// A value on a component that Bevy can animate.
/// A trait for exposing a value in an entity so that it can be animated.
///
/// You can implement this trait on a unit struct in order to support animating
/// custom components other than transforms and morph weights. Use that type in
/// conjunction with [`AnimatableCurve`] (and perhaps [`AnimatableKeyframeCurve`]
/// to define the animation itself).
/// For example, in order to animate field of view, you might use:
/// `AnimatableProperty` allows any value contained in an entity to be animated
/// as long as it can be obtained by mutable reference. This makes it more
/// flexible than [`animated_field`].
///
/// [`animated_field`]: crate::animated_field
///
/// Here, `AnimatableProperty` is used to animate a value inside an `Option`,
/// returning an error if the option is `None`.
///
/// # use bevy_animation::{prelude::AnimatableProperty, AnimationEntityMut, AnimationEvaluationError, animation_curves::EvaluatorId};
/// # use bevy_reflect::Reflect;
/// # use bevy_ecs::component::Component;
/// # use std::any::TypeId;
/// # use bevy_render::camera::{Projection, PerspectiveProjection};
/// #[derive(Reflect)]
/// struct FieldOfViewProperty;
/// #[derive(Component)]
/// struct ExampleComponent {
/// power_level: Option<f32>
/// }
///
/// impl AnimatableProperty for FieldOfViewProperty {
/// #[derive(Clone)]
/// struct PowerLevelProperty;
///
/// impl AnimatableProperty for PowerLevelProperty {
/// type Property = f32;
/// fn get_mut<'a>(&self, entity: &'a mut AnimationEntityMut) -> Result<&'a mut Self::Property, AnimationEvaluationError> {
/// fn get_mut<'a>(
/// &self,
/// entity: &'a mut AnimationEntityMut
/// ) -> Result<&'a mut Self::Property, AnimationEvaluationError> {
/// let component = entity
/// .get_mut::<Projection>()
/// .ok_or(AnimationEvaluationError::ComponentNotPresent(TypeId::of::<
/// Projection,
/// >(
/// )))?
/// .get_mut::<ExampleComponent>()
/// .ok_or(AnimationEvaluationError::ComponentNotPresent(
/// TypeId::of::<ExampleComponent>()
/// ))?
/// .into_inner();
/// match component {
/// Projection::Perspective(perspective) => Ok(&mut perspective.fov),
/// _ => Err(AnimationEvaluationError::PropertyNotPresent(TypeId::of::<
/// PerspectiveProjection,
/// >(
/// ))),
/// }
/// component.power_level.as_mut().ok_or(AnimationEvaluationError::PropertyNotPresent(
/// TypeId::of::<Option<f32>>()
/// ))
/// }
///
/// fn evaluator_id(&self) -> EvaluatorId {
@ -144,58 +149,44 @@ use downcast_rs::{impl_downcast, Downcast};
/// }
/// }
///
/// You can then create an [`AnimationClip`] to animate this property like so:
///
/// # use bevy_animation::{AnimationClip, AnimationTargetId, VariableCurve, AnimationEntityMut, AnimationEvaluationError, animation_curves::EvaluatorId};
/// You can then create an [`AnimatableCurve`] to animate this property like so:
///
/// # use bevy_animation::{VariableCurve, AnimationEntityMut, AnimationEvaluationError, animation_curves::EvaluatorId};
/// # use bevy_animation::prelude::{AnimatableProperty, AnimatableKeyframeCurve, AnimatableCurve};
/// # use bevy_ecs::name::Name;
/// # use bevy_reflect::Reflect;
/// # use bevy_render::camera::{Projection, PerspectiveProjection};
/// # use bevy_ecs::{name::Name, component::Component};
/// # use std::any::TypeId;
/// # let animation_target_id = AnimationTargetId::from(&Name::new("Test"));
/// # #[derive(Reflect, Clone)]
/// # struct FieldOfViewProperty;
/// # impl AnimatableProperty for FieldOfViewProperty {
/// # type Property = f32;
/// # fn get_mut<'a>(&self, entity: &'a mut AnimationEntityMut) -> Result<&'a mut Self::Property, AnimationEvaluationError> {
/// # let component = entity
/// # .get_mut::<Projection>()
/// # .ok_or(AnimationEvaluationError::ComponentNotPresent(TypeId::of::<
/// # Projection,
/// # >(
/// # )))?
/// # .into_inner();
/// # match component {
/// # Projection::Perspective(perspective) => Ok(&mut perspective.fov),
/// # _ => Err(AnimationEvaluationError::PropertyNotPresent(TypeId::of::<
/// # PerspectiveProjection,
/// # >(
/// # ))),
/// # }
/// # }
/// # fn evaluator_id(&self) -> EvaluatorId {
/// # EvaluatorId::Type(TypeId::of::<Self>())
/// # }
/// # #[derive(Component)]
/// # struct ExampleComponent { power_level: Option<f32> }
/// # #[derive(Clone)]
/// # struct PowerLevelProperty;
/// # impl AnimatableProperty for PowerLevelProperty {
/// # type Property = f32;
/// # fn get_mut<'a>(
/// # &self,
/// # entity: &'a mut AnimationEntityMut
/// # ) -> Result<&'a mut Self::Property, AnimationEvaluationError> {
/// # let component = entity
/// # .get_mut::<ExampleComponent>()
/// # .ok_or(AnimationEvaluationError::ComponentNotPresent(
/// # TypeId::of::<ExampleComponent>()
/// # ))?
/// # .into_inner();
/// # component.power_level.as_mut().ok_or(AnimationEvaluationError::PropertyNotPresent(
/// # TypeId::of::<Option<f32>>()
/// # ))
/// # }
/// # fn evaluator_id(&self) -> EvaluatorId {
/// # EvaluatorId::Type(TypeId::of::<Self>())
/// # }
/// # }
/// let mut animation_clip = AnimationClip::default();
/// animation_clip.add_curve_to_target(
/// animation_target_id,
/// AnimatableCurve::new(
/// FieldOfViewProperty,
/// AnimatableKeyframeCurve::new([
/// (0.0, core::f32::consts::PI / 4.0),
/// (1.0, core::f32::consts::PI / 3.0),
/// ]).expect("Failed to create font size curve")
/// )
/// AnimatableCurve::new(
/// PowerLevelProperty,
/// AnimatableKeyframeCurve::new([
/// (0.0, 0.0),
/// (1.0, 9001.0),
/// ]).expect("Failed to create power level curve")
/// );
///
/// Here, the use of [`AnimatableKeyframeCurve`] creates a curve out of the given keyframe time-value
/// pairs, using the [`Animatable`] implementation of `f32` to interpolate between them. The
/// invocation of [`AnimatableCurve::new`] with `FieldOfViewProperty` indicates that the `f32`
/// output from that curve is to be used to animate the font size of a `PerspectiveProjection` component (as
/// configured above).
///
/// [`AnimationClip`]: crate::AnimationClip
pub trait AnimatableProperty: Send + Sync + 'static {
/// The animated property type.
type Property: Animatable;

View File

@ -55,7 +55,7 @@ pub struct CubicKeyframeCurve<T> {
impl<V> Curve<V> for CubicKeyframeCurve<V>
where
V: VectorSpace,
V: VectorSpace<Scalar = f32>,
{
#[inline]
fn domain(&self) -> Interval {
@ -179,7 +179,7 @@ pub struct WideLinearKeyframeCurve<T> {
impl<T> IterableCurve<T> for WideLinearKeyframeCurve<T>
where
T: VectorSpace,
T: VectorSpace<Scalar = f32>,
{
#[inline]
fn domain(&self) -> Interval {
@ -289,7 +289,7 @@ pub struct WideCubicKeyframeCurve<T> {
impl<T> IterableCurve<T> for WideCubicKeyframeCurve<T>
where
T: VectorSpace,
T: VectorSpace<Scalar = f32>,
{
#[inline]
fn domain(&self) -> Interval {
@ -373,7 +373,7 @@ impl<T> WideCubicKeyframeCurve<T> {
/// recommended to use its implementation of the [`IterableCurve`] trait, which allows iterating
/// directly over information derived from the curve without allocating.
///
/// [`MorphWeights`]: bevy_render::prelude::MorphWeights
/// [`MorphWeights`]: bevy_mesh::morph::MorphWeights
#[derive(Debug, Clone, Reflect)]
#[reflect(Clone)]
pub enum WeightsCurve {
@ -406,7 +406,7 @@ fn cubic_spline_interpolation<T>(
step_duration: f32,
) -> T
where
T: VectorSpace,
T: VectorSpace<Scalar = f32>,
{
let coeffs = (vec4(2.0, 1.0, -2.0, 1.0) * lerp + vec4(-3.0, -2.0, 3.0, -1.0)) * lerp;
value_start * (coeffs.x * lerp + 1.0)
@ -415,7 +415,7 @@ where
+ tangent_in_end * step_duration * lerp * coeffs.w
}
fn cubic_spline_interpolate_slices<'a, T: VectorSpace>(
fn cubic_spline_interpolate_slices<'a, T: VectorSpace<Scalar = f32>>(
width: usize,
first: &'a [T],
second: &'a [T],

View File

@ -1,10 +1,11 @@
//! The animation graph, which allows animations to be blended together.
use core::{
fmt::Write,
iter,
ops::{Index, IndexMut, Range},
};
use std::io::{self, Write};
use std::io;
use bevy_asset::{
io::Reader, Asset, AssetEvent, AssetId, AssetLoader, AssetPath, Assets, Handle, LoadContext,
@ -17,7 +18,7 @@ use bevy_ecs::{
resource::Resource,
system::{Res, ResMut},
};
use bevy_platform_support::collections::HashMap;
use bevy_platform::collections::HashMap;
use bevy_reflect::{prelude::ReflectDefault, Reflect, ReflectSerialize};
use derive_more::derive::From;
use petgraph::{

View File

@ -1,8 +1,8 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
html_logo_url = "https://bevy.org/assets/icon.png",
html_favicon_url = "https://bevy.org/assets/icon.png"
)]
//! Animation for the game engine Bevy
@ -31,14 +31,14 @@ use crate::{
prelude::EvaluatorId,
};
use bevy_app::{Animation, App, Plugin, PostUpdate};
use bevy_asset::{Asset, AssetApp, AssetEvents, Assets};
use bevy_app::{AnimationSystems, App, Plugin, PostUpdate};
use bevy_asset::{Asset, AssetApp, AssetEventSystems, Assets};
use bevy_ecs::{prelude::*, world::EntityMutExcept};
use bevy_math::FloatOrd;
use bevy_platform_support::{collections::HashMap, hash::NoOpHash};
use bevy_platform::{collections::HashMap, hash::NoOpHash};
use bevy_reflect::{prelude::ReflectDefault, Reflect, TypePath};
use bevy_time::Time;
use bevy_transform::TransformSystem;
use bevy_transform::TransformSystems;
use bevy_utils::{PreHashMap, PreHashMapExt, TypeIdMap};
use petgraph::graph::NodeIndex;
use serde::{Deserialize, Serialize};
@ -324,13 +324,13 @@ impl AnimationClip {
.push(variable_curve);
}
/// Add a untargeted [`Event`] to this [`AnimationClip`].
/// Add an [`EntityEvent`] with no [`AnimationTarget`] to this [`AnimationClip`].
///
/// The `event` will be cloned and triggered on the [`AnimationPlayer`] entity once the `time` (in seconds)
/// is reached in the animation.
///
/// See also [`add_event_to_target`](Self::add_event_to_target).
pub fn add_event(&mut self, time: f32, event: impl Event + Clone) {
pub fn add_event(&mut self, time: f32, event: impl EntityEvent + Clone) {
self.add_event_fn(
time,
move |commands: &mut Commands, entity: Entity, _time: f32, _weight: f32| {
@ -339,7 +339,7 @@ impl AnimationClip {
);
}
/// Add an [`Event`] to an [`AnimationTarget`] named by an [`AnimationTargetId`].
/// Add an [`EntityEvent`] to an [`AnimationTarget`] named by an [`AnimationTargetId`].
///
/// The `event` will be cloned and triggered on the entity matching the target once the `time` (in seconds)
/// is reached in the animation.
@ -349,7 +349,7 @@ impl AnimationClip {
&mut self,
target_id: AnimationTargetId,
time: f32,
event: impl Event + Clone,
event: impl EntityEvent + Clone,
) {
self.add_event_fn_to_target(
target_id,
@ -360,19 +360,19 @@ impl AnimationClip {
);
}
/// Add a untargeted event function to this [`AnimationClip`].
/// Add an event function with no [`AnimationTarget`] to this [`AnimationClip`].
///
/// The `func` will trigger on the [`AnimationPlayer`] entity once the `time` (in seconds)
/// is reached in the animation.
///
/// For a simpler [`Event`]-based alternative, see [`AnimationClip::add_event`].
/// For a simpler [`EntityEvent`]-based alternative, see [`AnimationClip::add_event`].
/// See also [`add_event_to_target`](Self::add_event_to_target).
///
/// ```
/// # use bevy_animation::AnimationClip;
/// # let mut clip = AnimationClip::default();
/// clip.add_event_fn(1.0, |commands, entity, time, weight| {
/// println!("Animation Event Triggered {entity:#?} at time {time} with weight {weight}");
/// println!("Animation event triggered {entity:#?} at time {time} with weight {weight}");
/// })
/// ```
pub fn add_event_fn(
@ -388,14 +388,14 @@ impl AnimationClip {
/// The `func` will trigger on the entity matching the target once the `time` (in seconds)
/// is reached in the animation.
///
/// For a simpler [`Event`]-based alternative, see [`AnimationClip::add_event_to_target`].
/// For a simpler [`EntityEvent`]-based alternative, see [`AnimationClip::add_event_to_target`].
/// Use [`add_event`](Self::add_event) instead if you don't have a specific target.
///
/// ```
/// # use bevy_animation::{AnimationClip, AnimationTargetId};
/// # let mut clip = AnimationClip::default();
/// clip.add_event_fn_to_target(AnimationTargetId::from_iter(["Arm", "Hand"]), 1.0, |commands, entity, time, weight| {
/// println!("Animation Event Triggered {entity:#?} at time {time} with weight {weight}");
/// println!("Animation event triggered {entity:#?} at time {time} with weight {weight}");
/// })
/// ```
pub fn add_event_fn_to_target(
@ -685,7 +685,6 @@ impl ActiveAnimation {
#[reflect(Component, Default, Clone)]
pub struct AnimationPlayer {
active_animations: HashMap<AnimationNodeIndex, ActiveAnimation>,
blend_weights: HashMap<AnimationNodeIndex, f32>,
}
// This is needed since `#[derive(Clone)]` does not generate optimized `clone_from`.
@ -693,13 +692,11 @@ impl Clone for AnimationPlayer {
fn clone(&self) -> Self {
Self {
active_animations: self.active_animations.clone(),
blend_weights: self.blend_weights.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
self.active_animations.clone_from(&source.active_animations);
self.blend_weights.clone_from(&source.blend_weights);
}
}
@ -758,10 +755,10 @@ impl AnimationCurveEvaluators {
.component_property_curve_evaluators
.get_or_insert_with(component_property, func),
EvaluatorId::Type(type_id) => match self.type_id_curve_evaluators.entry(type_id) {
bevy_platform_support::collections::hash_map::Entry::Occupied(occupied_entry) => {
bevy_platform::collections::hash_map::Entry::Occupied(occupied_entry) => {
&mut **occupied_entry.into_mut()
}
bevy_platform_support::collections::hash_map::Entry::Vacant(vacant_entry) => {
bevy_platform::collections::hash_map::Entry::Vacant(vacant_entry) => {
&mut **vacant_entry.insert(func())
}
},
@ -1247,7 +1244,7 @@ impl Plugin for AnimationPlugin {
.add_systems(
PostUpdate,
(
graph::thread_animation_graphs.before(AssetEvents),
graph::thread_animation_graphs.before(AssetEventSystems),
advance_transitions,
advance_animations,
// TODO: `animate_targets` can animate anything, so
@ -1263,8 +1260,8 @@ impl Plugin for AnimationPlugin {
expire_completed_transitions,
)
.chain()
.in_set(Animation)
.before(TransformSystem::TransformPropagate),
.in_set(AnimationSystems)
.before(TransformSystems::Propagate),
);
}
}
@ -1533,9 +1530,11 @@ impl<'a> Iterator for TriggeredEventsIter<'a> {
#[cfg(test)]
mod tests {
use bevy_reflect::{DynamicMap, Map};
use super::*;
#[derive(Event, Reflect, Clone)]
#[derive(Event, EntityEvent, Reflect, Clone)]
struct A;
#[track_caller]
@ -1664,4 +1663,13 @@ mod tests {
active_animation.update(clip.duration, clip.duration); // 0.3 : 0.0
assert_triggered_events_with(&active_animation, &clip, [0.3, 0.2]);
}
#[test]
fn test_animation_node_index_as_key_of_dynamic_map() {
let mut map = DynamicMap::default();
map.insert_boxed(
Box::new(AnimationNodeIndex::new(0)),
Box::new(ActiveAnimation::default()),
);
}
}

View File

@ -118,8 +118,9 @@ pub fn advance_transitions(
// is divided between all the other layers, eventually culminating in the
// currently-playing animation receiving whatever's left. This results in a
// nicely normalized weight.
let mut remaining_weight = 1.0;
for (mut animation_transitions, mut player) in query.iter_mut() {
let mut remaining_weight = 1.0;
for transition in &mut animation_transitions.transitions.iter_mut().rev() {
// Decrease weight.
transition.current_weight = (transition.current_weight

View File

@ -0,0 +1,39 @@
[package]
name = "bevy_anti_aliasing"
version = "0.17.0-dev"
edition = "2024"
description = "Provides various anti aliasing implementations for Bevy Engine"
homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[features]
trace = []
webgl = []
webgpu = []
smaa_luts = ["bevy_render/ktx2", "bevy_image/ktx2", "bevy_image/zstd"]
[dependencies]
# bevy
bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
bevy_app = { path = "../bevy_app", version = "0.17.0-dev" }
bevy_image = { path = "../bevy_image", version = "0.17.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.17.0-dev" }
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.17.0-dev" }
# other
tracing = { version = "0.1", default-features = false, features = ["std"] }
[lints]
workspace = true
[package.metadata.docs.rs]
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
all-features = true

View File

@ -0,0 +1,7 @@
# Bevy Anti Aliasing
[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/bevyengine/bevy#license)
[![Crates.io](https://img.shields.io/crates/v/bevy_core_pipeline.svg)](https://crates.io/crates/bevy_core_pipeline)
[![Downloads](https://img.shields.io/crates/d/bevy_core_pipeline.svg)](https://crates.io/crates/bevy_core_pipeline)
[![Docs](https://docs.rs/bevy_core_pipeline/badge.svg)](https://docs.rs/bevy_core_pipeline/latest/bevy_core_pipeline/)
[![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy)

View File

@ -1,10 +1,10 @@
use crate::{
use bevy_app::prelude::*;
use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
use bevy_core_pipeline::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
};
use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_image::BevyDefault as _;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
@ -18,7 +18,7 @@ use bevy_render::{
},
renderer::RenderDevice,
view::{ExtractedView, ViewTarget},
Render, RenderApp, RenderSet,
Render, RenderApp, RenderSystems,
};
mod node;
@ -95,20 +95,12 @@ impl ExtractComponent for ContrastAdaptiveSharpening {
}
}
const CONTRAST_ADAPTIVE_SHARPENING_SHADER_HANDLE: Handle<Shader> =
weak_handle!("ef83f0a5-51df-4b51-9ab7-b5fd1ae5a397");
/// Adds Support for Contrast Adaptive Sharpening (CAS).
pub struct CasPlugin;
impl Plugin for CasPlugin {
fn build(&self, app: &mut App) {
load_internal_asset!(
app,
CONTRAST_ADAPTIVE_SHARPENING_SHADER_HANDLE,
"robust_contrast_adaptive_sharpening.wgsl",
Shader::from_wgsl
);
embedded_asset!(app, "robust_contrast_adaptive_sharpening.wgsl");
app.register_type::<ContrastAdaptiveSharpening>();
app.add_plugins((
@ -121,7 +113,7 @@ impl Plugin for CasPlugin {
};
render_app
.init_resource::<SpecializedRenderPipelines<CasPipeline>>()
.add_systems(Render, prepare_cas_pipelines.in_set(RenderSet::Prepare));
.add_systems(Render, prepare_cas_pipelines.in_set(RenderSystems::Prepare));
{
render_app
@ -171,6 +163,7 @@ impl Plugin for CasPlugin {
pub struct CasPipeline {
texture_bind_group: BindGroupLayout,
sampler: Sampler,
shader: Handle<Shader>,
}
impl FromWorld for CasPipeline {
@ -194,6 +187,7 @@ impl FromWorld for CasPipeline {
CasPipeline {
texture_bind_group,
sampler,
shader: load_embedded_asset!(render_world, "robust_contrast_adaptive_sharpening.wgsl"),
}
}
}
@ -217,7 +211,7 @@ impl SpecializedRenderPipeline for CasPipeline {
layout: vec![self.texture_bind_group.clone()],
vertex: fullscreen_shader_vertex_state(),
fragment: Some(FragmentState {
shader: CONTRAST_ADAPTIVE_SHARPENING_SHADER_HANDLE,
shader: self.shader.clone(),
shader_defs,
entry_point: "fragment".into(),
targets: vec![Some(ColorTargetState {

View File

@ -1,10 +1,10 @@
use crate::{
use bevy_app::prelude::*;
use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
use bevy_core_pipeline::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
};
use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_ecs::prelude::*;
use bevy_image::BevyDefault as _;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
@ -18,7 +18,7 @@ use bevy_render::{
},
renderer::RenderDevice,
view::{ExtractedView, ViewTarget},
Render, RenderApp, RenderSet,
Render, RenderApp, RenderSystems,
};
use bevy_utils::default;
@ -80,13 +80,11 @@ impl Default for Fxaa {
}
}
const FXAA_SHADER_HANDLE: Handle<Shader> = weak_handle!("fc58c0a8-01c0-46e9-94cc-83a794bae7b0");
/// Adds support for Fast Approximate Anti-Aliasing (FXAA)
pub struct FxaaPlugin;
impl Plugin for FxaaPlugin {
fn build(&self, app: &mut App) {
load_internal_asset!(app, FXAA_SHADER_HANDLE, "fxaa.wgsl", Shader::from_wgsl);
embedded_asset!(app, "fxaa.wgsl");
app.register_type::<Fxaa>();
app.add_plugins(ExtractComponentPlugin::<Fxaa>::default());
@ -96,7 +94,10 @@ impl Plugin for FxaaPlugin {
};
render_app
.init_resource::<SpecializedRenderPipelines<FxaaPipeline>>()
.add_systems(Render, prepare_fxaa_pipelines.in_set(RenderSet::Prepare))
.add_systems(
Render,
prepare_fxaa_pipelines.in_set(RenderSystems::Prepare),
)
.add_render_graph_node::<ViewNodeRunner<FxaaNode>>(Core3d, Node3d::Fxaa)
.add_render_graph_edges(
Core3d,
@ -129,6 +130,7 @@ impl Plugin for FxaaPlugin {
pub struct FxaaPipeline {
texture_bind_group: BindGroupLayout,
sampler: Sampler,
shader: Handle<Shader>,
}
impl FromWorld for FxaaPipeline {
@ -155,6 +157,7 @@ impl FromWorld for FxaaPipeline {
FxaaPipeline {
texture_bind_group,
sampler,
shader: load_embedded_asset!(render_world, "fxaa.wgsl"),
}
}
}
@ -180,7 +183,7 @@ impl SpecializedRenderPipeline for FxaaPipeline {
layout: vec![self.texture_bind_group.clone()],
vertex: fullscreen_shader_vertex_state(),
fragment: Some(FragmentState {
shader: FXAA_SHADER_HANDLE,
shader: self.shader.clone(),
shader_defs: vec![
format!("EDGE_THRESH_{}", key.edge_threshold.get_str()).into(),
format!("EDGE_THRESH_MIN_{}", key.edge_threshold_min.get_str()).into(),

View File

@ -0,0 +1,26 @@
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
#![forbid(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://bevy.org/assets/icon.png",
html_favicon_url = "https://bevy.org/assets/icon.png"
)]
use bevy_app::Plugin;
use contrast_adaptive_sharpening::CasPlugin;
use fxaa::FxaaPlugin;
use smaa::SmaaPlugin;
use taa::TemporalAntiAliasPlugin;
pub mod contrast_adaptive_sharpening;
pub mod fxaa;
pub mod smaa;
pub mod taa;
#[derive(Default)]
pub struct AntiAliasingPlugin;
impl Plugin for AntiAliasingPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.add_plugins((FxaaPlugin, SmaaPlugin, TemporalAntiAliasPlugin, CasPlugin));
}
}

View File

@ -29,16 +29,16 @@
//! * Compatibility with SSAA and MSAA.
//!
//! [SMAA]: https://www.iryoku.com/smaa/
#[cfg(not(feature = "smaa_luts"))]
use crate::tonemapping::lut_placeholder;
use crate::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
};
use bevy_app::{App, Plugin};
#[cfg(feature = "smaa_luts")]
use bevy_asset::load_internal_binary_asset;
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_asset::{embedded_asset, load_embedded_asset, weak_handle, Handle};
#[cfg(not(feature = "smaa_luts"))]
use bevy_core_pipeline::tonemapping::lut_placeholder;
use bevy_core_pipeline::{
core_2d::graph::{Core2d, Node2d},
core_3d::graph::{Core3d, Node3d},
};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::Component,
@ -76,12 +76,10 @@ use bevy_render::{
renderer::{RenderContext, RenderDevice, RenderQueue},
texture::{CachedTexture, GpuImage, TextureCache},
view::{ExtractedView, ViewTarget},
Render, RenderApp, RenderSet,
Render, RenderApp, RenderSystems,
};
use bevy_utils::prelude::default;
/// The handle of the `smaa.wgsl` shader.
const SMAA_SHADER_HANDLE: Handle<Shader> = weak_handle!("fdd9839f-1ab4-4e0d-88a0-240b67da2ddf");
/// The handle of the area LUT, a KTX2 format texture that SMAA uses internally.
const SMAA_AREA_LUT_TEXTURE_HANDLE: Handle<Image> =
weak_handle!("569c4d67-c7fa-4958-b1af-0836023603c0");
@ -147,6 +145,8 @@ struct SmaaEdgeDetectionPipeline {
postprocess_bind_group_layout: BindGroupLayout,
/// The bind group layout for data specific to this pass.
edge_detection_bind_group_layout: BindGroupLayout,
/// The shader asset handle.
shader: Handle<Shader>,
}
/// The pipeline data for phase 2 of SMAA: blending weight calculation.
@ -155,6 +155,8 @@ struct SmaaBlendingWeightCalculationPipeline {
postprocess_bind_group_layout: BindGroupLayout,
/// The bind group layout for data specific to this pass.
blending_weight_calculation_bind_group_layout: BindGroupLayout,
/// The shader asset handle.
shader: Handle<Shader>,
}
/// The pipeline data for phase 3 of SMAA: neighborhood blending.
@ -163,6 +165,8 @@ struct SmaaNeighborhoodBlendingPipeline {
postprocess_bind_group_layout: BindGroupLayout,
/// The bind group layout for data specific to this pass.
neighborhood_blending_bind_group_layout: BindGroupLayout,
/// The shader asset handle.
shader: Handle<Shader>,
}
/// A unique identifier for a set of SMAA pipelines.
@ -287,7 +291,7 @@ pub struct SmaaSpecializedRenderPipelines {
impl Plugin for SmaaPlugin {
fn build(&self, app: &mut App) {
// Load the shader.
load_internal_asset!(app, SMAA_SHADER_HANDLE, "smaa.wgsl", Shader::from_wgsl);
embedded_asset!(app, "smaa.wgsl");
// Load the two lookup textures. These are compressed textures in KTX2
// format.
@ -297,8 +301,6 @@ impl Plugin for SmaaPlugin {
SMAA_AREA_LUT_TEXTURE_HANDLE,
"SMAAAreaLUT.ktx2",
|bytes, _: String| Image::from_buffer(
#[cfg(all(debug_assertions, feature = "dds"))]
"SMAAAreaLUT".to_owned(),
bytes,
bevy_image::ImageType::Format(bevy_image::ImageFormat::Ktx2),
bevy_image::CompressedImageFormats::NONE,
@ -315,8 +317,6 @@ impl Plugin for SmaaPlugin {
SMAA_SEARCH_LUT_TEXTURE_HANDLE,
"SMAASearchLUT.ktx2",
|bytes, _: String| Image::from_buffer(
#[cfg(all(debug_assertions, feature = "dds"))]
"SMAASearchLUT".to_owned(),
bytes,
bevy_image::ImageType::Format(bevy_image::ImageFormat::Ktx2),
bevy_image::CompressedImageFormats::NONE,
@ -350,10 +350,10 @@ impl Plugin for SmaaPlugin {
.add_systems(
Render,
(
prepare_smaa_pipelines.in_set(RenderSet::Prepare),
prepare_smaa_uniforms.in_set(RenderSet::PrepareResources),
prepare_smaa_textures.in_set(RenderSet::PrepareResources),
prepare_smaa_bind_groups.in_set(RenderSet::PrepareBindGroups),
prepare_smaa_pipelines.in_set(RenderSystems::Prepare),
prepare_smaa_uniforms.in_set(RenderSystems::PrepareResources),
prepare_smaa_textures.in_set(RenderSystems::PrepareResources),
prepare_smaa_bind_groups.in_set(RenderSystems::PrepareBindGroups),
),
)
.add_render_graph_node::<ViewNodeRunner<SmaaNode>>(Core3d, Node3d::Smaa)
@ -435,18 +435,23 @@ impl FromWorld for SmaaPipelines {
),
);
let shader = load_embedded_asset!(world, "smaa.wgsl");
SmaaPipelines {
edge_detection: SmaaEdgeDetectionPipeline {
postprocess_bind_group_layout: postprocess_bind_group_layout.clone(),
edge_detection_bind_group_layout,
shader: shader.clone(),
},
blending_weight_calculation: SmaaBlendingWeightCalculationPipeline {
postprocess_bind_group_layout: postprocess_bind_group_layout.clone(),
blending_weight_calculation_bind_group_layout,
shader: shader.clone(),
},
neighborhood_blending: SmaaNeighborhoodBlendingPipeline {
postprocess_bind_group_layout,
neighborhood_blending_bind_group_layout,
shader,
},
}
}
@ -476,13 +481,13 @@ impl SpecializedRenderPipeline for SmaaEdgeDetectionPipeline {
self.edge_detection_bind_group_layout.clone(),
],
vertex: VertexState {
shader: SMAA_SHADER_HANDLE,
shader: self.shader.clone(),
shader_defs: shader_defs.clone(),
entry_point: "edge_detection_vertex_main".into(),
buffers: vec![],
},
fragment: Some(FragmentState {
shader: SMAA_SHADER_HANDLE,
shader: self.shader.clone(),
shader_defs,
entry_point: "luma_edge_detection_fragment_main".into(),
targets: vec![Some(ColorTargetState {
@ -536,13 +541,13 @@ impl SpecializedRenderPipeline for SmaaBlendingWeightCalculationPipeline {
self.blending_weight_calculation_bind_group_layout.clone(),
],
vertex: VertexState {
shader: SMAA_SHADER_HANDLE,
shader: self.shader.clone(),
shader_defs: shader_defs.clone(),
entry_point: "blending_weight_calculation_vertex_main".into(),
buffers: vec![],
},
fragment: Some(FragmentState {
shader: SMAA_SHADER_HANDLE,
shader: self.shader.clone(),
shader_defs,
entry_point: "blending_weight_calculation_fragment_main".into(),
targets: vec![Some(ColorTargetState {
@ -584,13 +589,13 @@ impl SpecializedRenderPipeline for SmaaNeighborhoodBlendingPipeline {
self.neighborhood_blending_bind_group_layout.clone(),
],
vertex: VertexState {
shader: SMAA_SHADER_HANDLE,
shader: self.shader.clone(),
shader_defs: shader_defs.clone(),
entry_point: "neighborhood_blending_vertex_main".into(),
buffers: vec![],
},
fragment: Some(FragmentState {
shader: SMAA_SHADER_HANDLE,
shader: self.shader.clone(),
shader_defs,
entry_point: "neighborhood_blending_fragment_main".into(),
targets: vec![Some(ColorTargetState {
@ -842,7 +847,7 @@ impl ViewNode for SmaaNode {
view_smaa_uniform_offset,
smaa_textures,
view_smaa_bind_groups,
): QueryItem<'w, Self::ViewQuery>,
): QueryItem<'w, '_, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let pipeline_cache = world.resource::<PipelineCache>();

View File

@ -1,11 +1,11 @@
use crate::{
use bevy_app::{App, Plugin};
use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
use bevy_core_pipeline::{
core_3d::graph::{Core3d, Node3d},
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
prelude::Camera3d,
prepass::{DepthPrepass, MotionVectorPrepass, ViewPrepassTextures},
};
use bevy_app::{App, Plugin};
use bevy_asset::{load_internal_asset, weak_handle, Handle};
use bevy_diagnostic::FrameCount;
use bevy_ecs::{
prelude::{Component, Entity, ReflectComponent},
@ -36,12 +36,10 @@ use bevy_render::{
sync_world::RenderEntity,
texture::{CachedTexture, TextureCache},
view::{ExtractedView, Msaa, ViewTarget},
ExtractSchedule, MainWorld, Render, RenderApp, RenderSet,
ExtractSchedule, MainWorld, Render, RenderApp, RenderSystems,
};
use tracing::warn;
const TAA_SHADER_HANDLE: Handle<Shader> = weak_handle!("fea20d50-86b6-4069-aa32-374346aec00c");
/// Plugin for temporal anti-aliasing.
///
/// See [`TemporalAntiAliasing`] for more details.
@ -49,7 +47,7 @@ pub struct TemporalAntiAliasPlugin;
impl Plugin for TemporalAntiAliasPlugin {
fn build(&self, app: &mut App) {
load_internal_asset!(app, TAA_SHADER_HANDLE, "taa.wgsl", Shader::from_wgsl);
embedded_asset!(app, "taa.wgsl");
app.register_type::<TemporalAntiAliasing>();
@ -64,9 +62,9 @@ impl Plugin for TemporalAntiAliasPlugin {
.add_systems(
Render,
(
prepare_taa_jitter_and_mip_bias.in_set(RenderSet::ManageViews),
prepare_taa_pipelines.in_set(RenderSet::Prepare),
prepare_taa_history_textures.in_set(RenderSet::PrepareResources),
prepare_taa_jitter.in_set(RenderSystems::ManageViews),
prepare_taa_pipelines.in_set(RenderSystems::Prepare),
prepare_taa_history_textures.in_set(RenderSystems::PrepareResources),
),
)
.add_render_graph_node::<ViewNodeRunner<TemporalAntiAliasNode>>(Core3d, Node3d::Taa)
@ -115,7 +113,6 @@ impl Plugin for TemporalAntiAliasPlugin {
///
/// # Usage Notes
///
/// The [`TemporalAntiAliasPlugin`] must be added to your app.
/// Any camera with this component must also disable [`Msaa`] by setting it to [`Msaa::Off`].
///
/// [Currently](https://github.com/bevyengine/bevy/issues/8423), TAA cannot be used with [`bevy_render::camera::OrthographicProjection`].
@ -128,11 +125,9 @@ impl Plugin for TemporalAntiAliasPlugin {
///
/// 1. Write particle motion vectors to the motion vectors prepass texture
/// 2. Render particles after TAA
///
/// If no [`MipBias`] component is attached to the camera, TAA will add a `MipBias(-1.0)` component.
#[derive(Component, Reflect, Clone)]
#[reflect(Component, Default, Clone)]
#[require(TemporalJitter, DepthPrepass, MotionVectorPrepass)]
#[require(TemporalJitter, MipBias, DepthPrepass, MotionVectorPrepass)]
#[doc(alias = "Taa")]
pub struct TemporalAntiAliasing {
/// Set to true to delete the saved temporal history (past frames).
@ -243,6 +238,7 @@ struct TaaPipeline {
taa_bind_group_layout: BindGroupLayout,
nearest_sampler: Sampler,
linear_sampler: Sampler,
shader: Handle<Shader>,
}
impl FromWorld for TaaPipeline {
@ -287,6 +283,7 @@ impl FromWorld for TaaPipeline {
taa_bind_group_layout,
nearest_sampler,
linear_sampler,
shader: load_embedded_asset!(world, "taa.wgsl"),
}
}
}
@ -319,7 +316,7 @@ impl SpecializedRenderPipeline for TaaPipeline {
layout: vec![self.taa_bind_group_layout.clone()],
vertex: fullscreen_shader_vertex_state(),
fragment: Some(FragmentState {
shader: TAA_SHADER_HANDLE,
shader: self.shader.clone(),
shader_defs,
entry_point: "taa".into(),
targets: vec![
@ -345,16 +342,11 @@ impl SpecializedRenderPipeline for TaaPipeline {
}
fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld>) {
let mut cameras_3d = main_world.query_filtered::<(
let mut cameras_3d = main_world.query::<(
RenderEntity,
&Camera,
&Projection,
&mut TemporalAntiAliasing,
), (
With<Camera3d>,
With<TemporalJitter>,
With<DepthPrepass>,
With<MotionVectorPrepass>,
Option<&mut TemporalAntiAliasing>,
)>();
for (entity, camera, camera_projection, mut taa_settings) in
@ -364,14 +356,12 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld
let mut entity_commands = commands
.get_entity(entity)
.expect("Camera entity wasn't synced.");
if camera.is_active && has_perspective_projection {
entity_commands.insert(taa_settings.clone());
taa_settings.reset = false;
if taa_settings.is_some() && camera.is_active && has_perspective_projection {
entity_commands.insert(taa_settings.as_deref().unwrap().clone());
taa_settings.as_mut().unwrap().reset = false;
} else {
// TODO: needs better strategy for cleaning up
entity_commands.remove::<(
TemporalAntiAliasing,
// components added in prepare systems (because `TemporalAntiAliasNode` does not query extracted components)
TemporalAntiAliasHistoryTextures,
TemporalAntiAliasPipelineId,
)>();
@ -379,13 +369,22 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld
}
}
fn prepare_taa_jitter_and_mip_bias(
fn prepare_taa_jitter(
frame_count: Res<FrameCount>,
mut query: Query<(Entity, &mut TemporalJitter, Option<&MipBias>), With<TemporalAntiAliasing>>,
mut commands: Commands,
mut query: Query<
&mut TemporalJitter,
(
With<TemporalAntiAliasing>,
With<Camera3d>,
With<TemporalJitter>,
With<DepthPrepass>,
With<MotionVectorPrepass>,
),
>,
) {
// Halton sequence (2, 3) - 0.5, skipping i = 0
// Halton sequence (2, 3) - 0.5
let halton_sequence = [
vec2(0.0, 0.0),
vec2(0.0, -0.16666666),
vec2(-0.25, 0.16666669),
vec2(0.25, -0.3888889),
@ -393,17 +392,12 @@ fn prepare_taa_jitter_and_mip_bias(
vec2(0.125, 0.2777778),
vec2(-0.125, -0.2777778),
vec2(0.375, 0.055555582),
vec2(-0.4375, 0.3888889),
];
let offset = halton_sequence[frame_count.0 as usize % halton_sequence.len()];
for (entity, mut jitter, mip_bias) in &mut query {
for mut jitter in &mut query {
jitter.offset = offset;
if mip_bias.is_none() {
commands.entity(entity).insert(MipBias(-1.0));
}
}
}

View File

@ -1,9 +1,9 @@
[package]
name = "bevy_app"
version = "0.16.0-dev"
version = "0.17.0-dev"
edition = "2024"
description = "Provides core App functionality for Bevy Engine"
homepage = "https://bevyengine.org"
homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
@ -47,9 +47,8 @@ std = [
"bevy_ecs/std",
"dep:ctrlc",
"downcast-rs/std",
"bevy_utils/std",
"bevy_tasks/std",
"bevy_platform_support/std",
"bevy_platform/std",
]
## `critical-section` provides the building blocks for synchronization primitives
@ -57,14 +56,14 @@ std = [
critical-section = [
"bevy_tasks/critical-section",
"bevy_ecs/critical-section",
"bevy_platform_support/critical-section",
"bevy_platform/critical-section",
"bevy_reflect?/critical-section",
]
## Enables use of browser APIs.
## Note this is currently only applicable on `wasm32` architectures.
web = [
"bevy_platform_support/web",
"bevy_platform/web",
"bevy_tasks/web",
"bevy_reflect?/web",
"dep:wasm-bindgen",
@ -72,16 +71,20 @@ web = [
"dep:console_error_panic_hook",
]
hotpatching = [
"bevy_ecs/hotpatching",
"dep:dioxus-devtools",
"dep:crossbeam-channel",
]
[dependencies]
# bevy
bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", default-features = false }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", default-features = false, optional = true }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", default-features = false, features = [
"alloc",
] }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false }
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev", default-features = false }
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", default-features = false, optional = true }
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev", default-features = false }
bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev", default-features = false }
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false }
# other
downcast-rs = { version = "2", default-features = false }
@ -90,8 +93,10 @@ variadics_please = "1.1"
tracing = { version = "0.1", default-features = false, optional = true }
log = { version = "0.4", default-features = false }
cfg-if = "1.0.0"
dioxus-devtools = { version = "0.7.0-alpha.1", optional = true }
crossbeam-channel = { version = "0.5.0", optional = true }
[target.'cfg(any(unix, windows))'.dependencies]
[target.'cfg(any(all(unix, not(target_os = "horizon")), windows))'.dependencies]
ctrlc = { version = "3.4.4", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]

View File

@ -10,14 +10,14 @@ use alloc::{
pub use bevy_derive::AppLabel;
use bevy_ecs::{
component::RequiredComponentsError,
error::{BevyError, SystemErrorContext},
error::{DefaultErrorHandler, ErrorHandler},
event::{event_update_system, EventCursor},
intern::Interned,
prelude::*,
schedule::{InternedSystemSet, ScheduleBuildSettings, ScheduleLabel},
system::{IntoObserverSystem, ScheduleSystem, SystemId, SystemInput},
};
use bevy_platform_support::collections::HashMap;
use bevy_platform::collections::HashMap;
use core::{fmt::Debug, num::NonZero, panic::AssertUnwindSafe};
use log::debug;
@ -86,6 +86,7 @@ pub struct App {
/// [`WinitPlugin`]: https://docs.rs/bevy/latest/bevy/winit/struct.WinitPlugin.html
/// [`ScheduleRunnerPlugin`]: https://docs.rs/bevy/latest/bevy/app/struct.ScheduleRunnerPlugin.html
pub(crate) runner: RunnerFn,
default_error_handler: Option<ErrorHandler>,
}
impl Debug for App {
@ -105,10 +106,13 @@ impl Default for App {
#[cfg(feature = "bevy_reflect")]
{
use bevy_ecs::observer::ObservedBy;
app.init_resource::<AppTypeRegistry>();
app.register_type::<Name>();
app.register_type::<ChildOf>();
app.register_type::<Children>();
app.register_type::<ObservedBy>();
}
#[cfg(feature = "reflect_functions")]
@ -118,7 +122,7 @@ impl Default for App {
app.add_systems(
First,
event_update_system
.in_set(bevy_ecs::event::EventUpdates)
.in_set(bevy_ecs::event::EventUpdateSystems)
.run_if(bevy_ecs::event::event_update_condition),
);
app.add_event::<AppExit>();
@ -144,6 +148,7 @@ impl App {
sub_apps: HashMap::default(),
},
runner: Box::new(run_once),
default_error_handler: None,
}
}
@ -339,7 +344,7 @@ impl App {
self
}
/// Initializes `T` event handling by inserting an event queue resource ([`Events::<T>`])
/// Initializes [`BufferedEvent`] handling for `T` by inserting an event queue resource ([`Events::<T>`])
/// and scheduling an [`event_update_system`] in [`First`].
///
/// See [`Events`] for information on how to define events.
@ -350,7 +355,7 @@ impl App {
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Event)]
/// # #[derive(Event, BufferedEvent)]
/// # struct MyEvent;
/// # let mut app = App::new();
/// #
@ -358,7 +363,7 @@ impl App {
/// ```
pub fn add_event<T>(&mut self) -> &mut Self
where
T: Event,
T: BufferedEvent,
{
self.main_mut().add_event::<T>();
self
@ -1116,7 +1121,12 @@ impl App {
}
/// Inserts a [`SubApp`] with the given label.
pub fn insert_sub_app(&mut self, label: impl AppLabel, sub_app: SubApp) {
pub fn insert_sub_app(&mut self, label: impl AppLabel, mut sub_app: SubApp) {
if let Some(handler) = self.default_error_handler {
sub_app
.world_mut()
.get_resource_or_insert_with(|| DefaultErrorHandler(handler));
}
self.sub_apps.sub_apps.insert(label.intern(), sub_app);
}
@ -1274,18 +1284,6 @@ impl App {
self
}
/// Set the global system error handler to use for systems that return a [`Result`].
///
/// See the [`bevy_ecs::error` module-level documentation](bevy_ecs::error)
/// for more information.
pub fn set_system_error_handler(
&mut self,
error_handler: fn(BevyError, SystemErrorContext),
) -> &mut Self {
self.main_mut().set_system_error_handler(error_handler);
self
}
/// Attempts to determine if an [`AppExit`] was raised since the last update.
///
/// Will attempt to return the first [`Error`](AppExit::Error) it encounters.
@ -1311,6 +1309,8 @@ impl App {
/// Spawns an [`Observer`] entity, which will watch for and respond to the given event.
///
/// `observer` can be any system whose first parameter is [`On`].
///
/// # Examples
///
/// ```rust
@ -1325,14 +1325,14 @@ impl App {
/// # friends_allowed: bool,
/// # };
/// #
/// # #[derive(Event)]
/// # #[derive(Event, EntityEvent)]
/// # struct Invite;
/// #
/// # #[derive(Component)]
/// # struct Friend;
/// #
/// // An observer system can be any system where the first parameter is a trigger
/// app.add_observer(|trigger: Trigger<Party>, friends: Query<Entity, With<Friend>>, mut commands: Commands| {
///
/// app.add_observer(|trigger: On<Party>, friends: Query<Entity, With<Friend>>, mut commands: Commands| {
/// if trigger.event().friends_allowed {
/// for friend in friends.iter() {
/// commands.trigger_targets(Invite, friend);
@ -1347,6 +1347,49 @@ impl App {
self.world_mut().add_observer(observer);
self
}
/// Gets the error handler to set for new supapps.
///
/// Note that the error handler of existing subapps may differ.
pub fn get_error_handler(&self) -> Option<ErrorHandler> {
self.default_error_handler
}
/// Set the [default error handler] for the all subapps (including the main one and future ones)
/// that do not have one.
///
/// May only be called once and should be set by the application, not by libraries.
///
/// The handler will be called when an error is produced and not otherwise handled.
///
/// # Panics
/// Panics if called multiple times.
///
/// # Example
/// ```
/// # use bevy_app::*;
/// # use bevy_ecs::error::warn;
/// # fn MyPlugins(_: &mut App) {}
/// App::new()
/// .set_error_handler(warn)
/// .add_plugins(MyPlugins)
/// .run();
/// ```
///
/// [default error handler]: bevy_ecs::error::DefaultErrorHandler
pub fn set_error_handler(&mut self, handler: ErrorHandler) -> &mut Self {
assert!(
self.default_error_handler.is_none(),
"`set_error_handler` called multiple times on same `App`"
);
self.default_error_handler = Some(handler);
for sub_app in self.sub_apps.iter_mut() {
sub_app
.world_mut()
.get_resource_or_insert_with(|| DefaultErrorHandler(handler));
}
self
}
}
type RunnerFn = Box<dyn FnOnce(App) -> AppExit>;
@ -1364,7 +1407,7 @@ fn run_once(mut app: App) -> AppExit {
app.should_exit().unwrap_or(AppExit::Success)
}
/// An event that indicates the [`App`] should exit. If one or more of these are present at the end of an update,
/// A [`BufferedEvent`] that indicates the [`App`] should exit. If one or more of these are present at the end of an update,
/// the [runner](App::set_runner) will end and ([maybe](App::run)) return control to the caller.
///
/// This event can be used to detect when an exit is requested. Make sure that systems listening
@ -1374,7 +1417,7 @@ fn run_once(mut app: App) -> AppExit {
/// This type is roughly meant to map to a standard definition of a process exit code (0 means success, not 0 means error). Due to portability concerns
/// (see [`ExitCode`](https://doc.rust-lang.org/std/process/struct.ExitCode.html) and [`process::exit`](https://doc.rust-lang.org/std/process/fn.exit.html#))
/// we only allow error codes between 1 and [255](u8::MAX).
#[derive(Event, Debug, Clone, Default, PartialEq, Eq)]
#[derive(Event, BufferedEvent, Debug, Clone, Default, PartialEq, Eq)]
pub enum AppExit {
/// [`App`] exited without any problems.
#[default]
@ -1442,9 +1485,9 @@ mod tests {
change_detection::{DetectChanges, ResMut},
component::Component,
entity::Entity,
event::{Event, EventWriter, Events},
event::{BufferedEvent, Event, EventWriter, Events},
lifecycle::RemovedComponents,
query::With,
removal_detection::RemovedComponents,
resource::Resource,
schedule::{IntoScheduleConfigs, ScheduleLabel},
system::{Commands, Query},
@ -1808,7 +1851,7 @@ mod tests {
}
#[test]
fn events_should_be_updated_once_per_update() {
#[derive(Event, Clone)]
#[derive(Event, BufferedEvent, Clone)]
struct TestEvent;
let mut app = App::new();

View File

@ -0,0 +1,42 @@
//! Utilities for hotpatching code.
extern crate alloc;
use alloc::sync::Arc;
use bevy_ecs::{event::EventWriter, HotPatched};
#[cfg(not(target_family = "wasm"))]
use dioxus_devtools::connect_subsecond;
use dioxus_devtools::subsecond;
pub use dioxus_devtools::subsecond::{call, HotFunction};
use crate::{Last, Plugin};
/// Plugin connecting to Dioxus CLI to enable hot patching.
#[derive(Default)]
pub struct HotPatchPlugin;
impl Plugin for HotPatchPlugin {
fn build(&self, app: &mut crate::App) {
let (sender, receiver) = crossbeam_channel::bounded::<HotPatched>(1);
// Connects to the dioxus CLI that will handle rebuilds
// This will open a connection to the dioxus CLI to receive updated jump tables
// Sends a `HotPatched` message through the channel when the jump table is updated
#[cfg(not(target_family = "wasm"))]
connect_subsecond();
subsecond::register_handler(Arc::new(move || {
sender.send(HotPatched).unwrap();
}));
// Adds a system that will read the channel for new `HotPatched`, and forward them as event to the ECS
app.add_event::<HotPatched>().add_systems(
Last,
move |mut events: EventWriter<HotPatched>| {
if receiver.try_recv().is_ok() {
events.write_default();
}
},
);
}
}

View File

@ -8,8 +8,8 @@
#![cfg_attr(any(docsrs, docsrs_dep), feature(doc_auto_cfg, rustdoc_internals))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
html_logo_url = "https://bevy.org/assets/icon.png",
html_favicon_url = "https://bevy.org/assets/icon.png"
)]
#![no_std]
@ -28,21 +28,26 @@ mod main_schedule;
mod panic_handler;
mod plugin;
mod plugin_group;
mod propagate;
mod schedule_runner;
mod sub_app;
mod task_pool_plugin;
#[cfg(all(any(unix, windows), feature = "std"))]
#[cfg(all(any(all(unix, not(target_os = "horizon")), windows), feature = "std"))]
mod terminal_ctrl_c_handler;
#[cfg(feature = "hotpatching")]
pub mod hotpatch;
pub use app::*;
pub use main_schedule::*;
pub use panic_handler::*;
pub use plugin::*;
pub use plugin_group::*;
pub use propagate::*;
pub use schedule_runner::*;
pub use sub_app::*;
pub use task_pool_plugin::*;
#[cfg(all(any(unix, windows), feature = "std"))]
#[cfg(all(any(all(unix, not(target_os = "horizon")), windows), feature = "std"))]
pub use terminal_ctrl_c_handler::*;
/// The app prelude.
@ -55,9 +60,9 @@ pub mod prelude {
main_schedule::{
First, FixedFirst, FixedLast, FixedPostUpdate, FixedPreUpdate, FixedUpdate, Last, Main,
PostStartup, PostUpdate, PreStartup, PreUpdate, RunFixedMainLoop,
RunFixedMainLoopSystem, SpawnScene, Startup, Update,
RunFixedMainLoopSystems, SpawnScene, Startup, Update,
},
sub_app::SubApp,
NonSendMarker, Plugin, PluginGroup, TaskPoolOptions, TaskPoolPlugin,
Plugin, PluginGroup, TaskPoolOptions, TaskPoolPlugin,
};
}

View File

@ -15,6 +15,13 @@ use bevy_ecs::{
/// By default, it will run the following schedules in the given order:
///
/// On the first run of the schedule (and only on the first run), it will run:
/// * [`StateTransition`] [^1]
/// * This means that [`OnEnter(MyState::Foo)`] will be called *before* [`PreStartup`]
/// if `MyState` was added to the app with `MyState::Foo` as the initial state,
/// as well as [`OnEnter(MyComputedState)`] if it `compute`s to `Some(Self)` in `MyState::Foo`.
/// * If you want to run systems before any state transitions, regardless of which state is the starting state,
/// for example, for registering required components, you can add your own custom startup schedule
/// before [`StateTransition`]. See [`MainScheduleOrder::insert_startup_before`] for more details.
/// * [`PreStartup`]
/// * [`Startup`]
/// * [`PostStartup`]
@ -22,7 +29,7 @@ use bevy_ecs::{
/// Then it will run:
/// * [`First`]
/// * [`PreUpdate`]
/// * [`StateTransition`]
/// * [`StateTransition`] [^1]
/// * [`RunFixedMainLoop`]
/// * This will run [`FixedMain`] zero to many times, based on how much time has elapsed.
/// * [`Update`]
@ -37,35 +44,39 @@ use bevy_ecs::{
///
/// See [`RenderPlugin`] and [`PipelinedRenderingPlugin`] for more details.
///
/// [^1]: [`StateTransition`] is inserted only if you have `bevy_state` feature enabled. It is enabled in `default` features.
///
/// [`StateTransition`]: https://docs.rs/bevy/latest/bevy/prelude/struct.StateTransition.html
/// [`OnEnter(MyState::Foo)`]: https://docs.rs/bevy/latest/bevy/prelude/struct.OnEnter.html
/// [`OnEnter(MyComputedState)`]: https://docs.rs/bevy/latest/bevy/prelude/struct.OnEnter.html
/// [`RenderPlugin`]: https://docs.rs/bevy/latest/bevy/render/struct.RenderPlugin.html
/// [`PipelinedRenderingPlugin`]: https://docs.rs/bevy/latest/bevy/render/pipelined_rendering/struct.PipelinedRenderingPlugin.html
/// [`SubApp`]: crate::SubApp
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct Main;
/// The schedule that runs before [`Startup`].
///
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct PreStartup;
/// The schedule that runs once when the app starts.
///
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct Startup;
/// The schedule that runs once after [`Startup`].
///
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct PostStartup;
/// Runs first in the schedule.
///
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct First;
/// The schedule that contains logic that must run before [`Update`]. For example, a system that reads raw keyboard
@ -76,33 +87,33 @@ pub struct First;
/// [`PreUpdate`] abstracts out "pre work implementation details".
///
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct PreUpdate;
/// Runs the [`FixedMain`] schedule in a loop according until all relevant elapsed time has been "consumed".
///
/// If you need to order your variable timestep systems
/// before or after the fixed update logic, use the [`RunFixedMainLoopSystem`] system set.
/// If you need to order your variable timestep systems before or after
/// the fixed update logic, use the [`RunFixedMainLoopSystems`] system set.
///
/// Note that in contrast to most other Bevy schedules, systems added directly to
/// [`RunFixedMainLoop`] will *not* be parallelized between each other.
///
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct RunFixedMainLoop;
/// Runs first in the [`FixedMain`] schedule.
///
/// See the [`FixedMain`] schedule for details on how fixed updates work.
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct FixedFirst;
/// The schedule that contains logic that must run before [`FixedUpdate`].
///
/// See the [`FixedMain`] schedule for details on how fixed updates work.
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct FixedPreUpdate;
/// The schedule that contains most gameplay logic, which runs at a fixed rate rather than every render frame.
@ -117,7 +128,7 @@ pub struct FixedPreUpdate;
/// See the [`Update`] schedule for examples of systems that *should not* use this schedule.
/// See the [`FixedMain`] schedule for details on how fixed updates work.
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct FixedUpdate;
/// The schedule that runs after the [`FixedUpdate`] schedule, for reacting
@ -125,26 +136,26 @@ pub struct FixedUpdate;
///
/// See the [`FixedMain`] schedule for details on how fixed updates work.
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct FixedPostUpdate;
/// The schedule that runs last in [`FixedMain`]
///
/// See the [`FixedMain`] schedule for details on how fixed updates work.
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct FixedLast;
/// The schedule that contains systems which only run after a fixed period of time has elapsed.
///
/// This is run by the [`RunFixedMainLoop`] schedule. If you need to order your variable timestep systems
/// before or after the fixed update logic, use the [`RunFixedMainLoopSystem`] system set.
/// before or after the fixed update logic, use the [`RunFixedMainLoopSystems`] system set.
///
/// Frequency of execution is configured by inserting `Time<Fixed>` resource, 64 Hz by default.
/// See [this example](https://github.com/bevyengine/bevy/blob/latest/examples/time/time.rs).
///
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct FixedMain;
/// The schedule that contains any app logic that must run once per render frame.
@ -157,13 +168,13 @@ pub struct FixedMain;
///
/// See the [`FixedUpdate`] schedule for examples of systems that *should not* use this schedule.
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct Update;
/// The schedule that contains scene spawning.
///
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct SpawnScene;
/// The schedule that contains logic that must run after [`Update`]. For example, synchronizing "local transforms" in a hierarchy
@ -174,18 +185,22 @@ pub struct SpawnScene;
/// [`PostUpdate`] abstracts out "implementation details" from users defining systems in [`Update`].
///
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct PostUpdate;
/// Runs last in the schedule.
///
/// See the [`Main`] schedule for some details about how schedules are run.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct Last;
/// Animation system set. This exists in [`PostUpdate`].
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
pub struct Animation;
pub struct AnimationSystems;
/// Deprecated alias for [`AnimationSystems`].
#[deprecated(since = "0.17.0", note = "Renamed to `AnimationSystems`.")]
pub type Animation = AnimationSystems;
/// Defines the schedules to be run for the [`Main`] schedule, including
/// their order.
@ -307,9 +322,9 @@ impl Plugin for MainSchedulePlugin {
.configure_sets(
RunFixedMainLoop,
(
RunFixedMainLoopSystem::BeforeFixedMainLoop,
RunFixedMainLoopSystem::FixedMainLoop,
RunFixedMainLoopSystem::AfterFixedMainLoop,
RunFixedMainLoopSystems::BeforeFixedMainLoop,
RunFixedMainLoopSystems::FixedMainLoop,
RunFixedMainLoopSystems::AfterFixedMainLoop,
)
.chain(),
);
@ -389,7 +404,7 @@ impl FixedMain {
/// Note that in contrast to most other Bevy schedules, systems added directly to
/// [`RunFixedMainLoop`] will *not* be parallelized between each other.
#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone, SystemSet)]
pub enum RunFixedMainLoopSystem {
pub enum RunFixedMainLoopSystems {
/// Runs before the fixed update logic.
///
/// A good example of a system that fits here
@ -408,7 +423,7 @@ pub enum RunFixedMainLoopSystem {
/// App::new()
/// .add_systems(
/// RunFixedMainLoop,
/// update_camera_rotation.in_set(RunFixedMainLoopSystem::BeforeFixedMainLoop))
/// update_camera_rotation.in_set(RunFixedMainLoopSystems::BeforeFixedMainLoop))
/// .add_systems(FixedUpdate, update_physics);
///
/// # fn update_camera_rotation() {}
@ -421,7 +436,7 @@ pub enum RunFixedMainLoopSystem {
///
/// Don't place systems here, use [`FixedUpdate`] and friends instead.
/// Use this system instead to order your systems to run specifically inbetween the fixed update logic and all
/// other systems that run in [`RunFixedMainLoopSystem::BeforeFixedMainLoop`] or [`RunFixedMainLoopSystem::AfterFixedMainLoop`].
/// other systems that run in [`RunFixedMainLoopSystems::BeforeFixedMainLoop`] or [`RunFixedMainLoopSystems::AfterFixedMainLoop`].
///
/// [`Time<Virtual>`]: https://docs.rs/bevy/latest/bevy/prelude/struct.Virtual.html
/// [`Time::overstep`]: https://docs.rs/bevy/latest/bevy/time/struct.Time.html#method.overstep
@ -437,8 +452,8 @@ pub enum RunFixedMainLoopSystem {
/// // This system will be called before all interpolation systems
/// // that third-party plugins might add.
/// prepare_for_interpolation
/// .after(RunFixedMainLoopSystem::FixedMainLoop)
/// .before(RunFixedMainLoopSystem::AfterFixedMainLoop),
/// .after(RunFixedMainLoopSystems::FixedMainLoop)
/// .before(RunFixedMainLoopSystems::AfterFixedMainLoop),
/// )
/// );
///
@ -462,10 +477,14 @@ pub enum RunFixedMainLoopSystem {
/// .add_systems(FixedUpdate, update_physics)
/// .add_systems(
/// RunFixedMainLoop,
/// interpolate_transforms.in_set(RunFixedMainLoopSystem::AfterFixedMainLoop));
/// interpolate_transforms.in_set(RunFixedMainLoopSystems::AfterFixedMainLoop));
///
/// # fn interpolate_transforms() {}
/// # fn update_physics() {}
/// ```
AfterFixedMainLoop,
}
/// Deprecated alias for [`RunFixedMainLoopSystems`].
#[deprecated(since = "0.17.0", note = "Renamed to `RunFixedMainLoopSystems`.")]
pub type RunFixedMainLoopSystem = RunFixedMainLoopSystems;

View File

@ -1,4 +1,4 @@
//! This module provides panic handlers for [Bevy](https://bevyengine.org)
//! This module provides panic handlers for [Bevy](https://bevy.org)
//! apps, and automatically configures platform specifics (i.e. Wasm or Android).
//!
//! By default, the [`PanicHandlerPlugin`] from this crate is included in Bevy's `DefaultPlugins`.

View File

@ -4,7 +4,7 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};
use bevy_platform_support::collections::hash_map::Entry;
use bevy_platform::collections::hash_map::Entry;
use bevy_utils::TypeIdMap;
use core::any::TypeId;
use log::{debug, warn};

View File

@ -0,0 +1,551 @@
use alloc::vec::Vec;
use core::marker::PhantomData;
use crate::{App, Plugin, Update};
use bevy_ecs::{
component::Component,
entity::Entity,
hierarchy::ChildOf,
lifecycle::RemovedComponents,
query::{Changed, Or, QueryFilter, With, Without},
relationship::{Relationship, RelationshipTarget},
schedule::{IntoScheduleConfigs, SystemSet},
system::{Commands, Local, Query},
};
/// Plugin to automatically propagate a component value to all direct and transient relationship
/// targets (e.g. [`bevy_ecs::hierarchy::Children`]) of entities with a [`Propagate`] component.
///
/// The plugin Will maintain the target component over hierarchy changes, adding or removing
/// `C` when a relationship `R` (e.g. [`ChildOf`]) is added to or removed from a
/// relationship tree with a [`Propagate<C>`] source, or if the [`Propagate<C>`] component
/// is added, changed or removed.
///
/// Optionally you can include a query filter `F` to restrict the entities that are updated.
/// Note that the filter is not rechecked dynamically: changes to the filter state will not be
/// picked up until the [`Propagate`] component is touched, or the hierarchy is changed.
/// All members of the tree between source and target must match the filter for propagation
/// to reach a given target.
/// Individual entities can be skipped or terminate the propagation with the [`PropagateOver`]
/// and [`PropagateStop`] components.
pub struct HierarchyPropagatePlugin<
C: Component + Clone + PartialEq,
F: QueryFilter = (),
R: Relationship = ChildOf,
>(PhantomData<fn() -> (C, F, R)>);
/// Causes the inner component to be added to this entity and all direct and transient relationship
/// targets. A target with a [`Propagate<C>`] component of its own will override propagation from
/// that point in the tree.
#[derive(Component, Clone, PartialEq)]
pub struct Propagate<C: Component + Clone + PartialEq>(pub C);
/// Stops the output component being added to this entity.
/// Relationship targets will still inherit the component from this entity or its parents.
#[derive(Component)]
pub struct PropagateOver<C>(PhantomData<fn() -> C>);
/// Stops the propagation at this entity. Children will not inherit the component.
#[derive(Component)]
pub struct PropagateStop<C>(PhantomData<fn() -> C>);
/// The set in which propagation systems are added. You can schedule your logic relative to this set.
#[derive(SystemSet, Clone, PartialEq, PartialOrd, Ord)]
pub struct PropagateSet<C: Component + Clone + PartialEq> {
_p: PhantomData<fn() -> C>,
}
/// Internal struct for managing propagation
#[derive(Component, Clone, PartialEq)]
pub struct Inherited<C: Component + Clone + PartialEq>(pub C);
impl<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship> Default
for HierarchyPropagatePlugin<C, F, R>
{
fn default() -> Self {
Self(Default::default())
}
}
impl<C> Default for PropagateOver<C> {
fn default() -> Self {
Self(Default::default())
}
}
impl<C> Default for PropagateStop<C> {
fn default() -> Self {
Self(Default::default())
}
}
impl<C: Component + Clone + PartialEq> core::fmt::Debug for PropagateSet<C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PropagateSet")
.field("_p", &self._p)
.finish()
}
}
impl<C: Component + Clone + PartialEq> Eq for PropagateSet<C> {}
impl<C: Component + Clone + PartialEq> core::hash::Hash for PropagateSet<C> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self._p.hash(state);
}
}
impl<C: Component + Clone + PartialEq> Default for PropagateSet<C> {
fn default() -> Self {
Self {
_p: Default::default(),
}
}
}
impl<C: Component + Clone + PartialEq, F: QueryFilter + 'static, R: Relationship> Plugin
for HierarchyPropagatePlugin<C, F, R>
{
fn build(&self, app: &mut App) {
app.add_systems(
Update,
(
update_source::<C, F>,
update_stopped::<C, F>,
update_reparented::<C, F, R>,
propagate_inherited::<C, F, R>,
propagate_output::<C, F>,
)
.chain()
.in_set(PropagateSet::<C>::default()),
);
}
}
/// add/remove `Inherited::<C>` and `C` for entities with a direct `Propagate::<C>`
pub fn update_source<C: Component + Clone + PartialEq, F: QueryFilter>(
mut commands: Commands,
changed: Query<
(Entity, &Propagate<C>),
(
Or<(Changed<Propagate<C>>, Without<Inherited<C>>)>,
Without<PropagateStop<C>>,
),
>,
mut removed: RemovedComponents<Propagate<C>>,
) {
for (entity, source) in &changed {
commands
.entity(entity)
.try_insert(Inherited(source.0.clone()));
}
for removed in removed.read() {
if let Ok(mut commands) = commands.get_entity(removed) {
commands.remove::<(Inherited<C>, C)>();
}
}
}
/// remove `Inherited::<C>` and `C` for entities with a `PropagateStop::<C>`
pub fn update_stopped<C: Component + Clone + PartialEq, F: QueryFilter>(
mut commands: Commands,
q: Query<Entity, (With<Inherited<C>>, With<PropagateStop<C>>, F)>,
) {
for entity in q.iter() {
let mut cmds = commands.entity(entity);
cmds.remove::<(Inherited<C>, C)>();
}
}
/// add/remove `Inherited::<C>` and `C` for entities which have changed relationship
pub fn update_reparented<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>(
mut commands: Commands,
moved: Query<
(Entity, &R, Option<&Inherited<C>>),
(
Changed<R>,
Without<Propagate<C>>,
Without<PropagateStop<C>>,
F,
),
>,
relations: Query<&Inherited<C>>,
orphaned: Query<Entity, (With<Inherited<C>>, Without<Propagate<C>>, Without<R>, F)>,
) {
for (entity, relation, maybe_inherited) in &moved {
if let Ok(inherited) = relations.get(relation.get()) {
commands.entity(entity).try_insert(inherited.clone());
} else if maybe_inherited.is_some() {
commands.entity(entity).remove::<(Inherited<C>, C)>();
}
}
for orphan in &orphaned {
commands.entity(orphan).remove::<(Inherited<C>, C)>();
}
}
/// add/remove `Inherited::<C>` for targets of entities with modified `Inherited::<C>`
pub fn propagate_inherited<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>(
mut commands: Commands,
changed: Query<
(&Inherited<C>, &R::RelationshipTarget),
(Changed<Inherited<C>>, Without<PropagateStop<C>>, F),
>,
recurse: Query<
(Option<&R::RelationshipTarget>, Option<&Inherited<C>>),
(Without<Propagate<C>>, Without<PropagateStop<C>>, F),
>,
mut removed: RemovedComponents<Inherited<C>>,
mut to_process: Local<Vec<(Entity, Option<Inherited<C>>)>>,
) {
// gather changed
for (inherited, targets) in &changed {
to_process.extend(
targets
.iter()
.map(|target| (target, Some(inherited.clone()))),
);
}
// and removed
for entity in removed.read() {
if let Ok((Some(targets), _)) = recurse.get(entity) {
to_process.extend(targets.iter().map(|target| (target, None)));
}
}
// propagate
while let Some((entity, maybe_inherited)) = (*to_process).pop() {
let Ok((maybe_targets, maybe_current)) = recurse.get(entity) else {
continue;
};
if maybe_current == maybe_inherited.as_ref() {
continue;
}
if let Some(targets) = maybe_targets {
to_process.extend(
targets
.iter()
.map(|target| (target, maybe_inherited.clone())),
);
}
if let Some(inherited) = maybe_inherited {
commands.entity(entity).try_insert(inherited.clone());
} else {
commands.entity(entity).remove::<(Inherited<C>, C)>();
}
}
}
/// add `C` to entities with `Inherited::<C>`
pub fn propagate_output<C: Component + Clone + PartialEq, F: QueryFilter>(
mut commands: Commands,
changed: Query<
(Entity, &Inherited<C>, Option<&C>),
(Changed<Inherited<C>>, Without<PropagateOver<C>>, F),
>,
) {
for (entity, inherited, maybe_current) in &changed {
if maybe_current.is_some_and(|c| &inherited.0 == c) {
continue;
}
commands.entity(entity).try_insert(inherited.0.clone());
}
}
#[cfg(test)]
mod tests {
use bevy_ecs::schedule::Schedule;
use crate::{App, Update};
use super::*;
#[derive(Component, Clone, PartialEq, Debug)]
struct TestValue(u32);
#[test]
fn test_simple_propagate() {
let mut app = App::new();
app.add_schedule(Schedule::new(Update));
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::default());
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
let intermediate = app
.world_mut()
.spawn_empty()
.insert(ChildOf(propagator))
.id();
let propagatee = app
.world_mut()
.spawn_empty()
.insert(ChildOf(intermediate))
.id();
app.update();
assert!(app
.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee)
.is_ok());
}
#[test]
fn test_reparented() {
let mut app = App::new();
app.add_schedule(Schedule::new(Update));
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::default());
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
let propagatee = app
.world_mut()
.spawn_empty()
.insert(ChildOf(propagator))
.id();
app.update();
assert!(app
.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee)
.is_ok());
}
#[test]
fn test_reparented_with_prior() {
let mut app = App::new();
app.add_schedule(Schedule::new(Update));
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::default());
let propagator_a = app.world_mut().spawn(Propagate(TestValue(1))).id();
let propagator_b = app.world_mut().spawn(Propagate(TestValue(2))).id();
let propagatee = app
.world_mut()
.spawn_empty()
.insert(ChildOf(propagator_a))
.id();
app.update();
assert_eq!(
app.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee),
Ok(&TestValue(1))
);
app.world_mut()
.commands()
.entity(propagatee)
.insert(ChildOf(propagator_b));
app.update();
assert_eq!(
app.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee),
Ok(&TestValue(2))
);
}
#[test]
fn test_remove_orphan() {
let mut app = App::new();
app.add_schedule(Schedule::new(Update));
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::default());
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
let propagatee = app
.world_mut()
.spawn_empty()
.insert(ChildOf(propagator))
.id();
app.update();
assert!(app
.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee)
.is_ok());
app.world_mut()
.commands()
.entity(propagatee)
.remove::<ChildOf>();
app.update();
assert!(app
.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee)
.is_err());
}
#[test]
fn test_remove_propagated() {
let mut app = App::new();
app.add_schedule(Schedule::new(Update));
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::default());
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
let propagatee = app
.world_mut()
.spawn_empty()
.insert(ChildOf(propagator))
.id();
app.update();
assert!(app
.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee)
.is_ok());
app.world_mut()
.commands()
.entity(propagator)
.remove::<Propagate<TestValue>>();
app.update();
assert!(app
.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee)
.is_err());
}
#[test]
fn test_propagate_over() {
let mut app = App::new();
app.add_schedule(Schedule::new(Update));
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::default());
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
let propagate_over = app
.world_mut()
.spawn(TestValue(2))
.insert(ChildOf(propagator))
.id();
let propagatee = app
.world_mut()
.spawn_empty()
.insert(ChildOf(propagate_over))
.id();
app.update();
assert_eq!(
app.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee),
Ok(&TestValue(1))
);
}
#[test]
fn test_propagate_stop() {
let mut app = App::new();
app.add_schedule(Schedule::new(Update));
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::default());
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
let propagate_stop = app
.world_mut()
.spawn(PropagateStop::<TestValue>::default())
.insert(ChildOf(propagator))
.id();
let no_propagatee = app
.world_mut()
.spawn_empty()
.insert(ChildOf(propagate_stop))
.id();
app.update();
assert!(app
.world_mut()
.query::<&TestValue>()
.get(app.world(), no_propagatee)
.is_err());
}
#[test]
fn test_intermediate_override() {
let mut app = App::new();
app.add_schedule(Schedule::new(Update));
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::default());
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
let intermediate = app
.world_mut()
.spawn_empty()
.insert(ChildOf(propagator))
.id();
let propagatee = app
.world_mut()
.spawn_empty()
.insert(ChildOf(intermediate))
.id();
app.update();
assert_eq!(
app.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee),
Ok(&TestValue(1))
);
app.world_mut()
.entity_mut(intermediate)
.insert(Propagate(TestValue(2)));
app.update();
assert_eq!(
app.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee),
Ok(&TestValue(2))
);
}
#[test]
fn test_filter() {
#[derive(Component)]
struct Marker;
let mut app = App::new();
app.add_schedule(Schedule::new(Update));
app.add_plugins(HierarchyPropagatePlugin::<TestValue, With<Marker>>::default());
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
let propagatee = app
.world_mut()
.spawn_empty()
.insert(ChildOf(propagator))
.id();
app.update();
assert!(app
.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee)
.is_err());
// NOTE: changes to the filter condition are not rechecked
app.world_mut().entity_mut(propagator).insert(Marker);
app.world_mut().entity_mut(propagatee).insert(Marker);
app.update();
assert!(app
.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee)
.is_err());
app.world_mut()
.entity_mut(propagator)
.insert(Propagate(TestValue(1)));
app.update();
assert!(app
.world_mut()
.query::<&TestValue>()
.get(app.world(), propagatee)
.is_ok());
}
}

View File

@ -3,7 +3,7 @@ use crate::{
plugin::Plugin,
PluginsState,
};
use bevy_platform_support::time::Instant;
use bevy_platform::time::Instant;
use core::time::Duration;
#[cfg(all(target_arch = "wasm32", feature = "web"))]
@ -159,9 +159,8 @@ impl Plugin for ScheduleRunnerPlugin {
} else {
loop {
match tick(&mut app, wait) {
Ok(Some(_delay)) => {
#[cfg(feature = "std")]
std::thread::sleep(_delay);
Ok(Some(delay)) => {
bevy_platform::thread::sleep(delay);
}
Ok(None) => continue,
Err(exit) => return exit,

View File

@ -1,13 +1,12 @@
use crate::{App, AppLabel, InternedAppLabel, Plugin, Plugins, PluginsState};
use alloc::{boxed::Box, string::String, vec::Vec};
use bevy_ecs::{
error::{DefaultSystemErrorHandler, SystemErrorContext},
event::EventRegistry,
prelude::*,
schedule::{InternedScheduleLabel, InternedSystemSet, ScheduleBuildSettings, ScheduleLabel},
system::{ScheduleSystem, SystemId, SystemInput},
};
use bevy_platform_support::collections::{HashMap, HashSet};
use bevy_platform::collections::{HashMap, HashSet};
use core::fmt::Debug;
#[cfg(feature = "trace")]
@ -336,26 +335,10 @@ impl SubApp {
self
}
/// Set the global error handler to use for systems that return a [`Result`].
///
/// See the [`bevy_ecs::error` module-level documentation](bevy_ecs::error)
/// for more information.
pub fn set_system_error_handler(
&mut self,
error_handler: fn(BevyError, SystemErrorContext),
) -> &mut Self {
let mut default_handler = self
.world_mut()
.get_resource_or_init::<DefaultSystemErrorHandler>();
default_handler.0 = error_handler;
self
}
/// See [`App::add_event`].
pub fn add_event<T>(&mut self) -> &mut Self
where
T: Event,
T: BufferedEvent,
{
if !self.world.contains_resource::<Events<T>>() {
EventRegistry::register_event::<T>(self.world_mut());

View File

@ -1,20 +1,21 @@
use crate::{App, Plugin};
use alloc::string::ToString;
use bevy_platform_support::sync::Arc;
use bevy_platform::sync::Arc;
use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder};
use core::{fmt::Debug, marker::PhantomData};
use core::fmt::Debug;
use log::trace;
cfg_if::cfg_if! {
if #[cfg(not(all(target_arch = "wasm32", feature = "web")))] {
use {crate::Last, bevy_ecs::prelude::NonSend, bevy_tasks::tick_global_task_pools_on_main_thread};
use {crate::Last, bevy_tasks::tick_global_task_pools_on_main_thread};
use bevy_ecs::system::NonSendMarker;
/// A system used to check and advanced our task pools.
///
/// Calls [`tick_global_task_pools_on_main_thread`],
/// and uses [`NonSendMarker`] to ensure that this system runs on the main thread
fn tick_global_task_pools(_main_thread_marker: Option<NonSend<NonSendMarker>>) {
fn tick_global_task_pools(_main_thread_marker: NonSendMarker) {
tick_global_task_pools_on_main_thread();
}
}
@ -36,8 +37,6 @@ impl Plugin for TaskPoolPlugin {
_app.add_systems(Last, tick_global_task_pools);
}
}
/// A dummy type that is [`!Send`](Send), to force systems to run on the main thread.
pub struct NonSendMarker(PhantomData<*mut ()>);
/// Defines a simple way to determine how many threads to use given the number of remaining cores
/// and number of total cores

View File

@ -1,9 +1,9 @@
[package]
name = "bevy_asset"
version = "0.16.0-dev"
version = "0.17.0-dev"
edition = "2024"
description = "Provides asset functionality for Bevy Engine"
homepage = "https://bevyengine.org"
homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
@ -11,7 +11,7 @@ keywords = ["bevy"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
file_watcher = ["notify-debouncer-full", "watch"]
file_watcher = ["notify-debouncer-full", "watch", "multi_threaded"]
embedded_watcher = ["file_watcher"]
multi_threaded = ["bevy_tasks/multi_threaded"]
asset_processor = []
@ -19,42 +19,53 @@ watch = []
trace = []
[dependencies]
bevy_app = { path = "../bevy_app", version = "0.16.0-dev" }
bevy_asset_macros = { path = "macros", version = "0.16.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev" }
bevy_log = { path = "../bevy_log", version = "0.16.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [
bevy_app = { path = "../bevy_app", version = "0.17.0-dev", default-features = false, features = [
"bevy_reflect",
] }
bevy_asset_macros = { path = "macros", version = "0.17.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev", default-features = false }
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", default-features = false, features = [
"uuid",
] }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" }
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev", default-features = false, features = [
"async_executor",
] }
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev", default-features = false }
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
"std",
] }
stackfuture = "0.3"
atomicow = "1.0"
async-broadcast = "0.7.2"
async-fs = "2.0"
async-lock = "3.0"
bitflags = { version = "2.3", features = ["serde"] }
crossbeam-channel = "0.5"
downcast-rs = { version = "2", default-features = false, features = ["std"] }
disqualified = "1.0"
either = "1.13"
futures-io = "0.3"
futures-lite = "2.0.1"
blake3 = "1.5"
parking_lot = { version = "0.12", features = ["arc_lock", "send_guard"] }
ron = "0.8"
serde = { version = "1", features = ["derive"] }
stackfuture = { version = "0.3", default-features = false }
atomicow = { version = "1.1", default-features = false, features = ["std"] }
async-broadcast = { version = "0.7.2", default-features = false }
async-fs = { version = "2.0", default-features = false }
async-lock = { version = "3.0", default-features = false }
bitflags = { version = "2.3", default-features = false }
crossbeam-channel = { version = "0.5", default-features = false, features = [
"std",
] }
downcast-rs = { version = "2", default-features = false }
disqualified = { version = "1.0", default-features = false }
either = { version = "1.13", default-features = false }
futures-io = { version = "0.3", default-features = false }
futures-lite = { version = "2.0.1", default-features = false }
blake3 = { version = "1.5", default-features = false }
parking_lot = { version = "0.12", default-features = false, features = [
"arc_lock",
"send_guard",
] }
ron = { version = "0.10", default-features = false }
serde = { version = "1", default-features = false, features = ["derive"] }
thiserror = { version = "2", default-features = false }
derive_more = { version = "1", default-features = false, features = ["from"] }
uuid = { version = "1.13.1", features = ["v4"] }
tracing = { version = "0.1", default-features = false, features = ["std"] }
uuid = { version = "1.13.1", default-features = false, features = [
"v4",
"serde",
] }
tracing = { version = "0.1", default-features = false }
[target.'cfg(target_os = "android")'.dependencies]
bevy_window = { path = "../bevy_window", version = "0.16.0-dev" }
bevy_window = { path = "../bevy_window", version = "0.17.0-dev" }
[target.'cfg(target_arch = "wasm32")'.dependencies]
# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption.
@ -67,18 +78,18 @@ web-sys = { version = "0.3", features = [
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
uuid = { version = "1.13.1", default-features = false, features = ["js"] }
bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [
bevy_app = { path = "../bevy_app", version = "0.17.0-dev", default-features = false, features = [
"web",
] }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev", default-features = false, features = [
bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev", default-features = false, features = [
"web",
] }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", default-features = false, features = [
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev", default-features = false, features = [
"web",
] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
notify-debouncer-full = { version = "0.5.0", optional = true }
notify-debouncer-full = { version = "0.5.0", default-features = false, optional = true }
[lints]
workspace = true

View File

@ -1,9 +1,9 @@
[package]
name = "bevy_asset_macros"
version = "0.16.0-dev"
version = "0.17.0-dev"
edition = "2024"
description = "Derive implementations for bevy_asset"
homepage = "https://bevyengine.org"
homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
@ -12,7 +12,7 @@ keywords = ["bevy"]
proc-macro = true
[dependencies]
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.16.0-dev" }
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.17.0-dev" }
syn = "2.0"
proc-macro2 = "1.0"

View File

@ -1,6 +1,7 @@
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
//! Macros for deriving asset traits.
use bevy_macro_utils::BevyManifest;
use proc_macro::{Span, TokenStream};
use quote::{format_ident, quote};
@ -12,6 +13,7 @@ pub(crate) fn bevy_asset_path() -> Path {
const DEPENDENCY_ATTRIBUTE: &str = "dependency";
/// Implement the `Asset` trait.
#[proc_macro_derive(Asset, attributes(dependency))]
pub fn derive_asset(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
@ -30,6 +32,7 @@ pub fn derive_asset(input: TokenStream) -> TokenStream {
})
}
/// Implement the `VisitAssetDependencies` trait.
#[proc_macro_derive(VisitAssetDependencies, attributes(dependency))]
pub fn derive_asset_dependency_visitor(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);

View File

@ -13,7 +13,7 @@ use bevy_ecs::{
storage::{Table, TableRow},
world::unsafe_world_cell::UnsafeWorldCell,
};
use bevy_platform_support::collections::HashMap;
use bevy_platform::collections::HashMap;
use core::marker::PhantomData;
use disqualified::ShortName;
use tracing::error;
@ -22,10 +22,10 @@ use tracing::error;
/// the [`AssetChanged`] filter to determine if an asset has changed since the last time
/// a query ran.
///
/// This resource is automatically managed by the [`AssetEvents`](crate::AssetEvents) schedule and
/// should not be exposed to the user in order to maintain safety guarantees. Any additional uses of
/// this resource should be carefully audited to ensure that they do not introduce any safety
/// issues.
/// This resource is automatically managed by the [`AssetEventSystems`](crate::AssetEventSystems)
/// system set and should not be exposed to the user in order to maintain safety guarantees.
/// Any additional uses of this resource should be carefully audited to ensure that they do not
/// introduce any safety issues.
#[derive(Resource)]
pub(crate) struct AssetChanges<A: Asset> {
change_ticks: HashMap<AssetId<A>, Tick>,
@ -102,14 +102,13 @@ impl<'w, A: AsAssetId> AssetChangeCheck<'w, A> {
///
/// # Quirks
///
/// - Asset changes are registered in the [`AssetEvents`] schedule.
/// - Asset changes are registered in the [`AssetEventSystems`] system set.
/// - Removed assets are not detected.
///
/// The list of changed assets only gets updated in the
/// [`AssetEvents`] schedule which runs in `Last`. Therefore, `AssetChanged`
/// will only pick up asset changes in schedules following `AssetEvents` or the
/// next frame. Consider adding the system in the `Last` schedule after [`AssetEvents`] if you need
/// to react without frame delay to asset changes.
/// The list of changed assets only gets updated in the [`AssetEventSystems`] system set,
/// which runs in `PostUpdate`. Therefore, `AssetChanged` will only pick up asset changes in schedules
/// following [`AssetEventSystems`] or the next frame. Consider adding the system in the `Last` schedule
/// after [`AssetEventSystems`] if you need to react without frame delay to asset changes.
///
/// # Performance
///
@ -120,7 +119,7 @@ impl<'w, A: AsAssetId> AssetChangeCheck<'w, A> {
///
/// If no `A` asset updated since the last time the system ran, then no lookups occur.
///
/// [`AssetEvents`]: crate::AssetEvents
/// [`AssetEventSystems`]: crate::AssetEventSystems
/// [`Assets<Mesh>::get_mut`]: crate::Assets::get_mut
pub struct AssetChanged<A: AsAssetId>(PhantomData<A>);
@ -159,14 +158,14 @@ unsafe impl<A: AsAssetId> WorldQuery for AssetChanged<A> {
fetch
}
unsafe fn init_fetch<'w>(
unsafe fn init_fetch<'w, 's>(
world: UnsafeWorldCell<'w>,
state: &Self::State,
state: &'s Self::State,
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w> {
// SAFETY:
// - `AssetChanges` is private and only accessed mutably in the `AssetEvents` schedule
// - `AssetChanges` is private and only accessed mutably in the `AssetEventSystems` system set.
// - `resource_id` was obtained from the type ID of `AssetChanges<A::Asset>`.
let Some(changes) = (unsafe {
world
@ -202,9 +201,9 @@ unsafe impl<A: AsAssetId> WorldQuery for AssetChanged<A> {
const IS_DENSE: bool = <&A>::IS_DENSE;
unsafe fn set_archetype<'w>(
unsafe fn set_archetype<'w, 's>(
fetch: &mut Self::Fetch<'w>,
state: &Self::State,
state: &'s Self::State,
archetype: &'w Archetype,
table: &'w Table,
) {
@ -216,7 +215,11 @@ unsafe impl<A: AsAssetId> WorldQuery for AssetChanged<A> {
}
}
unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) {
unsafe fn set_table<'w, 's>(
fetch: &mut Self::Fetch<'w>,
state: &Self::State,
table: &'w Table,
) {
if let Some(inner) = &mut fetch.inner {
// SAFETY: We delegate to the inner `set_table` for `A`
unsafe {
@ -266,6 +269,7 @@ unsafe impl<A: AsAssetId> QueryFilter for AssetChanged<A> {
#[inline]
unsafe fn filter_fetch(
state: &Self::State,
fetch: &mut Self::Fetch<'_>,
entity: Entity,
table_row: TableRow,
@ -273,7 +277,7 @@ unsafe impl<A: AsAssetId> QueryFilter for AssetChanged<A> {
fetch.inner.as_mut().is_some_and(|inner| {
// SAFETY: We delegate to the inner `fetch` for `A`
unsafe {
let handle = <&A>::fetch(inner, entity, table_row);
let handle = <&A>::fetch(&state.asset_id, inner, entity, table_row);
fetch.check.has_changed(handle)
}
})
@ -283,7 +287,7 @@ unsafe impl<A: AsAssetId> QueryFilter for AssetChanged<A> {
#[cfg(test)]
#[expect(clippy::print_stdout, reason = "Allowed in tests.")]
mod tests {
use crate::{AssetEvents, AssetPlugin, Handle};
use crate::{AssetEventSystems, AssetPlugin, Handle};
use alloc::{vec, vec::Vec};
use core::num::NonZero;
use std::println;
@ -406,7 +410,7 @@ mod tests {
.init_asset::<MyAsset>()
.insert_resource(Counter(vec![0, 0, 0, 0]))
.add_systems(Update, add_some)
.add_systems(PostUpdate, count_update.after(AssetEvents));
.add_systems(PostUpdate, count_update.after(AssetEventSystems));
// First run of the app, `add_systems(Startup…)` runs.
app.update(); // run_count == 0
@ -441,7 +445,7 @@ mod tests {
},
)
.add_systems(Update, update_some)
.add_systems(PostUpdate, count_update.after(AssetEvents));
.add_systems(PostUpdate, count_update.after(AssetEventSystems));
// First run of the app, `add_systems(Startup…)` runs.
app.update(); // run_count == 0

View File

@ -6,7 +6,7 @@ use bevy_ecs::{
resource::Resource,
system::{Res, ResMut, SystemChangeTick},
};
use bevy_platform_support::collections::HashMap;
use bevy_platform::collections::HashMap;
use bevy_reflect::{Reflect, TypePath};
use core::{any::TypeId, iter::Enumerate, marker::PhantomData, sync::atomic::AtomicU32};
use crossbeam_channel::{Receiver, Sender};
@ -437,6 +437,18 @@ impl<A: Asset> Assets<A> {
result
}
/// Retrieves a mutable reference to the [`Asset`] with the given `id`, if it exists.
///
/// This is the same as [`Assets::get_mut`] except it doesn't emit [`AssetEvent::Modified`].
#[inline]
pub fn get_mut_untracked(&mut self, id: impl Into<AssetId<A>>) -> Option<&mut A> {
let id: AssetId<A> = id.into();
match id {
AssetId::Index { index, .. } => self.dense_storage.get_mut(index),
AssetId::Uuid { uuid } => self.hash_map.get_mut(&uuid),
}
}
/// Removes (and returns) the [`Asset`] with the given `id`, if it exists.
/// Note that this supports anything that implements `Into<AssetId<A>>`, which includes [`Handle`] and [`AssetId`].
pub fn remove(&mut self, id: impl Into<AssetId<A>>) -> Option<A> {
@ -450,6 +462,8 @@ impl<A: Asset> Assets<A> {
/// Removes (and returns) the [`Asset`] with the given `id`, if it exists. This skips emitting [`AssetEvent::Removed`].
/// Note that this supports anything that implements `Into<AssetId<A>>`, which includes [`Handle`] and [`AssetId`].
///
/// This is the same as [`Assets::remove`] except it doesn't emit [`AssetEvent::Removed`].
pub fn remove_untracked(&mut self, id: impl Into<AssetId<A>>) -> Option<A> {
let id: AssetId<A> = id.into();
self.duplicate_handles.remove(&id);
@ -462,16 +476,22 @@ impl<A: Asset> Assets<A> {
/// Removes the [`Asset`] with the given `id`.
pub(crate) fn remove_dropped(&mut self, id: AssetId<A>) {
match self.duplicate_handles.get_mut(&id) {
None | Some(0) => {}
None => {}
Some(0) => {
self.duplicate_handles.remove(&id);
}
Some(value) => {
*value -= 1;
return;
}
}
let existed = match id {
AssetId::Index { index, .. } => self.dense_storage.remove_dropped(index).is_some(),
AssetId::Uuid { uuid } => self.hash_map.remove(&uuid).is_some(),
};
self.queued_events.push(AssetEvent::Unused { id });
if existed {
self.queued_events.push(AssetEvent::Removed { id });
}
@ -553,7 +573,6 @@ impl<A: Asset> Assets<A> {
}
}
assets.queued_events.push(AssetEvent::Unused { id });
assets.remove_dropped(id);
}
}
@ -595,7 +614,7 @@ impl<A: Asset> Assets<A> {
pub struct AssetsMutIterator<'a, A: Asset> {
queued_events: &'a mut Vec<AssetEvent<A>>,
dense_storage: Enumerate<core::slice::IterMut<'a, Entry<A>>>,
hash_map: bevy_platform_support::collections::hash_map::IterMut<'a, Uuid, A>,
hash_map: bevy_platform::collections::hash_map::IterMut<'a, Uuid, A>,
}
impl<'a, A: Asset> Iterator for AssetsMutIterator<'a, A> {

View File

@ -1,12 +1,12 @@
use crate::{Asset, AssetId, AssetLoadError, AssetPath, UntypedAssetId};
use bevy_ecs::event::Event;
use bevy_ecs::event::{BufferedEvent, Event};
use bevy_reflect::Reflect;
use core::fmt::Debug;
/// An event emitted when a specific [`Asset`] fails to load.
/// A [`BufferedEvent`] emitted when a specific [`Asset`] fails to load.
///
/// For an untyped equivalent, see [`UntypedAssetLoadFailedEvent`].
#[derive(Event, Clone, Debug)]
#[derive(Event, BufferedEvent, Clone, Debug)]
pub struct AssetLoadFailedEvent<A: Asset> {
/// The stable identifier of the asset that failed to load.
pub id: AssetId<A>,
@ -24,7 +24,7 @@ impl<A: Asset> AssetLoadFailedEvent<A> {
}
/// An untyped version of [`AssetLoadFailedEvent`].
#[derive(Event, Clone, Debug)]
#[derive(Event, BufferedEvent, Clone, Debug)]
pub struct UntypedAssetLoadFailedEvent {
/// The stable identifier of the asset that failed to load.
pub id: UntypedAssetId,
@ -44,9 +44,9 @@ impl<A: Asset> From<&AssetLoadFailedEvent<A>> for UntypedAssetLoadFailedEvent {
}
}
/// Events that occur for a specific loaded [`Asset`], such as "value changed" events and "dependency" events.
/// [`BufferedEvent`]s that occur for a specific loaded [`Asset`], such as "value changed" events and "dependency" events.
#[expect(missing_docs, reason = "Documenting the id fields is unhelpful.")]
#[derive(Event, Reflect)]
#[derive(Event, BufferedEvent, Reflect)]
pub enum AssetEvent<A: Asset> {
/// Emitted whenever an [`Asset`] is added.
Added { id: AssetId<A> },

View File

@ -11,7 +11,6 @@ use core::{
use crossbeam_channel::{Receiver, Sender};
use disqualified::ShortName;
use thiserror::Error;
use uuid::Uuid;
/// Provides [`Handle`] and [`UntypedHandle`] _for a specific asset type_.
/// This should _only_ be used for one specific asset type.
@ -149,14 +148,6 @@ impl<T: Asset> Clone for Handle<T> {
}
impl<A: Asset> Handle<A> {
/// Create a new [`Handle::Weak`] with the given [`u128`] encoding of a [`Uuid`].
#[deprecated = "use the `weak_handle!` macro with a UUID string instead"]
pub const fn weak_from_u128(value: u128) -> Self {
Handle::Weak(AssetId::Uuid {
uuid: Uuid::from_u128(value),
})
}
/// Returns the [`AssetId`] of this [`Asset`].
#[inline]
pub fn id(&self) -> AssetId<A> {
@ -289,7 +280,7 @@ impl<A: Asset> From<&mut Handle<A>> for UntypedAssetId {
/// to be stored together and compared.
///
/// See [`Handle`] for more information.
#[derive(Clone)]
#[derive(Clone, Reflect)]
pub enum UntypedHandle {
/// A strong handle, which will keep the referenced [`Asset`] alive until all strong handles are dropped.
Strong(Arc<StrongHandle>),
@ -548,9 +539,10 @@ pub enum UntypedAssetConversionError {
#[cfg(test)]
mod tests {
use alloc::boxed::Box;
use bevy_platform_support::hash::FixedHasher;
use bevy_platform::hash::FixedHasher;
use bevy_reflect::PartialReflect;
use core::hash::BuildHasher;
use uuid::Uuid;
use super::*;

View File

@ -164,7 +164,7 @@ impl<A: Asset> From<AssetIndex> for AssetId<A> {
/// An "untyped" / "generic-less" [`Asset`] identifier that behaves much like [`AssetId`], but stores the [`Asset`] type
/// information at runtime instead of compile-time. This increases the size of the type, but it enables storing asset ids
/// across asset types together and enables comparisons between them.
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, Reflect)]
pub enum UntypedAssetId {
/// A small / efficient runtime identifier that can be used to efficiently look up an asset stored in [`Assets`]. This is
/// the "default" identifier used for assets. The alternative(s) (ex: [`UntypedAssetId::Uuid`]) will only be used if assets are
@ -441,7 +441,7 @@ mod tests {
fn hash<T: Hash>(data: &T) -> u64 {
use core::hash::BuildHasher;
bevy_platform_support::hash::FixedHasher.hash_one(data)
bevy_platform::hash::FixedHasher.hash_one(data)
}
/// Typed and Untyped `AssetIds` should be equivalent to each other and themselves

View File

@ -4,7 +4,7 @@ use crate::io::{
AssetSourceEvent, AssetWatcher,
};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use bevy_platform_support::collections::HashMap;
use bevy_platform::collections::HashMap;
use core::time::Duration;
use notify_debouncer_full::{notify::RecommendedWatcher, Debouncer, RecommendedCache};
use parking_lot::RwLock;

View File

@ -8,8 +8,10 @@ use crate::io::{
memory::{Dir, MemoryAssetReader, Value},
AssetSource, AssetSourceBuilders,
};
use crate::AssetServer;
use alloc::boxed::Box;
use bevy_ecs::resource::Resource;
use bevy_app::App;
use bevy_ecs::{resource::Resource, world::World};
use std::path::{Path, PathBuf};
#[cfg(feature = "embedded_watcher")]
@ -29,7 +31,7 @@ pub struct EmbeddedAssetRegistry {
dir: Dir,
#[cfg(feature = "embedded_watcher")]
root_paths: alloc::sync::Arc<
parking_lot::RwLock<bevy_platform_support::collections::HashMap<Box<Path>, PathBuf>>,
parking_lot::RwLock<bevy_platform::collections::HashMap<Box<Path>, PathBuf>>,
>,
}
@ -132,6 +134,71 @@ impl EmbeddedAssetRegistry {
}
}
/// Trait for the [`load_embedded_asset!`] macro, to access [`AssetServer`]
/// from arbitrary things.
///
/// [`load_embedded_asset!`]: crate::load_embedded_asset
pub trait GetAssetServer {
fn get_asset_server(&self) -> &AssetServer;
}
impl GetAssetServer for App {
fn get_asset_server(&self) -> &AssetServer {
self.world().get_asset_server()
}
}
impl GetAssetServer for World {
fn get_asset_server(&self) -> &AssetServer {
self.resource()
}
}
impl GetAssetServer for AssetServer {
fn get_asset_server(&self) -> &AssetServer {
self
}
}
/// Load an [embedded asset](crate::embedded_asset).
///
/// This is useful if the embedded asset in question is not publicly exposed, but
/// you need to use it internally.
///
/// # Syntax
///
/// This macro takes two arguments and an optional third one:
/// 1. The asset source. It may be `AssetServer`, `World` or `App`.
/// 2. The path to the asset to embed, as a string literal.
/// 3. Optionally, a closure of the same type as in [`AssetServer::load_with_settings`].
/// Consider explicitly typing the closure argument in case of type error.
///
/// # Usage
///
/// The advantage compared to using directly [`AssetServer::load`] is:
/// - This also accepts [`World`] and [`App`] arguments.
/// - This uses the exact same path as `embedded_asset!`, so you can keep it
/// consistent.
///
/// As a rule of thumb:
/// - If the asset in used in the same module as it is declared using `embedded_asset!`,
/// use this macro.
/// - Otherwise, use `AssetServer::load`.
#[macro_export]
macro_rules! load_embedded_asset {
(@get: $path: literal, $provider: expr) => {{
let path = $crate::embedded_path!($path);
let path = $crate::AssetPath::from_path_buf(path).with_source("embedded");
let asset_server = $crate::io::embedded::GetAssetServer::get_asset_server($provider);
(path, asset_server)
}};
($provider: expr, $path: literal, $settings: expr) => {{
let (path, asset_server) = $crate::load_embedded_asset!(@get: $path, $provider);
asset_server.load_with_settings(path, $settings)
}};
($provider: expr, $path: literal) => {{
let (path, asset_server) = $crate::load_embedded_asset!(@get: $path, $provider);
asset_server.load(path)
}};
}
/// Returns the [`Path`] for a given `embedded` asset.
/// This is used internally by [`embedded_asset`] and can be used to get a [`Path`]
/// that matches the [`AssetPath`](crate::AssetPath) used by that asset.
@ -140,7 +207,7 @@ impl EmbeddedAssetRegistry {
#[macro_export]
macro_rules! embedded_path {
($path_str: expr) => {{
embedded_path!("src", $path_str)
$crate::embedded_path!("src", $path_str)
}};
($source_path: expr, $path_str: expr) => {{
@ -168,6 +235,13 @@ pub fn _embedded_asset_path(
file_path: &Path,
asset_path: &Path,
) -> PathBuf {
let file_path = if cfg!(not(target_family = "windows")) {
// Work around bug: https://github.com/bevyengine/bevy/issues/14246
// Note, this will break any paths on Linux/Mac containing "\"
PathBuf::from(file_path.to_str().unwrap().replace("\\", "/"))
} else {
PathBuf::from(file_path)
};
let mut maybe_parent = file_path.parent();
let after_src = loop {
let Some(parent) = maybe_parent else {
@ -185,7 +259,7 @@ pub fn _embedded_asset_path(
/// Creates a new `embedded` asset by embedding the bytes of the given path into the current binary
/// and registering those bytes with the `embedded` [`AssetSource`].
///
/// This accepts the current [`App`](bevy_app::App) as the first parameter and a path `&str` (relative to the current file) as the second.
/// This accepts the current [`App`] as the first parameter and a path `&str` (relative to the current file) as the second.
///
/// By default this will generate an [`AssetPath`] using the following rules:
///
@ -210,14 +284,19 @@ pub fn _embedded_asset_path(
///
/// `embedded_asset!(app, "rock.wgsl")`
///
/// `rock.wgsl` can now be loaded by the [`AssetServer`](crate::AssetServer) with the following path:
/// `rock.wgsl` can now be loaded by the [`AssetServer`] as follows:
///
/// ```no_run
/// # use bevy_asset::{Asset, AssetServer};
/// # use bevy_asset::{Asset, AssetServer, load_embedded_asset};
/// # use bevy_reflect::TypePath;
/// # let asset_server: AssetServer = panic!();
/// # #[derive(Asset, TypePath)]
/// # struct Shader;
/// // If we are loading the shader in the same module we used `embedded_asset!`:
/// let shader = load_embedded_asset!(&asset_server, "rock.wgsl");
/// # let _: bevy_asset::Handle<Shader> = shader;
///
/// // If the goal is to expose the asset **to the end user**:
/// let shader = asset_server.load::<Shader>("embedded://bevy_rock/render/rock.wgsl");
/// ```
///
@ -251,11 +330,11 @@ pub fn _embedded_asset_path(
/// [`embedded_path`]: crate::embedded_path
#[macro_export]
macro_rules! embedded_asset {
($app: ident, $path: expr) => {{
($app: expr, $path: expr) => {{
$crate::embedded_asset!($app, "src", $path)
}};
($app: ident, $source_path: expr, $path: expr) => {{
($app: expr, $source_path: expr, $path: expr) => {{
let mut embedded = $app
.world_mut()
.resource_mut::<$crate::io::embedded::EmbeddedAssetRegistry>();

View File

@ -74,6 +74,15 @@ impl AssetReader for FileAssetReader {
return None;
}
}
// filter out hidden files. they are not listed by default but are directly targetable
if path
.file_name()
.and_then(|file_name| file_name.to_str())
.map(|file_name| file_name.starts_with('.'))
.unwrap_or_default()
{
return None;
}
let relative_path = path.strip_prefix(&root_path).unwrap();
Some(relative_path.to_owned())
})

View File

@ -35,7 +35,7 @@ impl FileWatcher {
sender: Sender<AssetSourceEvent>,
debounce_wait_time: Duration,
) -> Result<Self, notify::Error> {
let root = normalize_path(&path).canonicalize().unwrap();
let root = normalize_path(&path).canonicalize()?;
let watcher = new_asset_event_debouncer(
path.clone(),
debounce_wait_time,
@ -262,7 +262,7 @@ impl FilesystemEventHandler for FileEventHandler {
self.last_event = None;
}
fn get_path(&self, absolute_path: &Path) -> Option<(PathBuf, bool)> {
let absolute_path = absolute_path.canonicalize().unwrap();
let absolute_path = absolute_path.canonicalize().ok()?;
Some(get_asset_path(&self.root, &absolute_path))
}

View File

@ -145,6 +145,16 @@ impl AssetReader for FileAssetReader {
return None;
}
}
// filter out hidden files. they are not listed by default but are directly targetable
if path
.file_name()
.and_then(|file_name| file_name.to_str())
.map(|file_name| file_name.starts_with('.'))
.unwrap_or_default()
{
return None;
}
let relative_path = path.strip_prefix(&root_path).unwrap();
Some(relative_path.to_owned())
})

View File

@ -1,6 +1,6 @@
use crate::io::{AssetReader, AssetReaderError, PathStream, Reader};
use alloc::{boxed::Box, sync::Arc};
use bevy_platform_support::collections::HashMap;
use bevy_platform::collections::HashMap;
use crossbeam_channel::{Receiver, Sender};
use parking_lot::RwLock;
use std::path::Path;

View File

@ -1,6 +1,6 @@
use crate::io::{AssetReader, AssetReaderError, PathStream, Reader};
use alloc::{borrow::ToOwned, boxed::Box, sync::Arc, vec::Vec};
use bevy_platform_support::collections::HashMap;
use bevy_platform::collections::HashMap;
use core::{pin::Pin, task::Poll};
use futures_io::AsyncRead;
use futures_lite::{ready, Stream};
@ -60,8 +60,7 @@ impl Dir {
dir = self.get_or_insert_dir(parent);
}
let key: Box<str> = path.file_name().unwrap().to_string_lossy().into();
let data = dir.0.write().assets.remove(&key);
data
dir.0.write().assets.remove(&key)
}
pub fn insert_meta(&self, path: &Path, value: impl Into<Value>) {

View File

@ -9,7 +9,7 @@ use alloc::{
};
use atomicow::CowArc;
use bevy_ecs::resource::Resource;
use bevy_platform_support::collections::HashMap;
use bevy_platform::collections::HashMap;
use core::{fmt::Display, hash::Hash, time::Duration};
use thiserror::Error;
use tracing::{error, warn};

View File

@ -52,7 +52,7 @@ fn js_value_to_err(context: &str) -> impl FnOnce(JsValue) -> std::io::Error + '_
}
impl HttpWasmAssetReader {
async fn fetch_bytes<'a>(&self, path: PathBuf) -> Result<impl Reader, AssetReaderError> {
async fn fetch_bytes(&self, path: PathBuf) -> Result<impl Reader, AssetReaderError> {
// The JS global scope includes a self-reference via a specializing name, which can be used to determine the type of global context available.
let global: Global = js_sys::global().unchecked_into();
let promise = if !global.window().is_undefined() {
@ -81,7 +81,10 @@ impl HttpWasmAssetReader {
let reader = VecReader::new(bytes);
Ok(reader)
}
404 => Err(AssetReaderError::NotFound(path)),
// Some web servers, including itch.io's CDN, return 403 when a requested file isn't present.
// TODO: remove handling of 403 as not found when it's easier to configure
// see https://github.com/bevyengine/bevy/pull/19268#pullrequestreview-2882410105
403 | 404 => Err(AssetReaderError::NotFound(path)),
status => Err(AssetReaderError::HttpError(status)),
}
}

View File

@ -141,8 +141,8 @@
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
html_logo_url = "https://bevy.org/assets/icon.png",
html_favicon_url = "https://bevy.org/assets/icon.png"
)]
#![no_std]
@ -223,18 +223,11 @@ use bevy_ecs::{
schedule::{IntoScheduleConfigs, SystemSet},
world::FromWorld,
};
use bevy_platform_support::collections::HashSet;
use bevy_platform::collections::HashSet;
use bevy_reflect::{FromReflect, GetTypeRegistration, Reflect, TypePath};
use core::any::TypeId;
use tracing::error;
#[cfg(all(feature = "file_watcher", not(feature = "multi_threaded")))]
compile_error!(
"The \"file_watcher\" feature for hot reloading requires the \
\"multi_threaded\" feature to be functional.\n\
Consider either disabling the \"file_watcher\" feature or enabling \"multi_threaded\""
);
/// Provides "asset" loading and processing functionality. An [`Asset`] is a "runtime value" that is loaded from an [`AssetSource`],
/// which can be something like a filesystem, a network, etc.
///
@ -258,6 +251,33 @@ pub struct AssetPlugin {
pub mode: AssetMode,
/// How/If asset meta files should be checked.
pub meta_check: AssetMetaCheck,
/// How to handle load requests of files that are outside the approved directories.
///
/// Approved folders are [`AssetPlugin::file_path`] and the folder of each
/// [`AssetSource`](io::AssetSource). Subfolders within these folders are also valid.
pub unapproved_path_mode: UnapprovedPathMode,
}
/// Determines how to react to attempts to load assets not inside the approved folders.
///
/// Approved folders are [`AssetPlugin::file_path`] and the folder of each
/// [`AssetSource`](io::AssetSource). Subfolders within these folders are also valid.
///
/// It is strongly discouraged to use [`Allow`](UnapprovedPathMode::Allow) if your
/// app will include scripts or modding support, as it could allow allow arbitrary file
/// access for malicious code.
///
/// See [`AssetPath::is_unapproved`](crate::AssetPath::is_unapproved)
#[derive(Clone, Default)]
pub enum UnapprovedPathMode {
/// Unapproved asset loading is allowed. This is strongly discouraged.
Allow,
/// Fails to load any asset that is is unapproved, unless an override method is used, like
/// [`AssetServer::load_override`].
Deny,
/// Fails to load any asset that is is unapproved.
#[default]
Forbid,
}
/// Controls whether or not assets are pre-processed before being loaded.
@ -311,6 +331,7 @@ impl Default for AssetPlugin {
processed_file_path: Self::DEFAULT_PROCESSED_FILE_PATH.to_string(),
watch_for_changes_override: None,
meta_check: AssetMetaCheck::default(),
unapproved_path_mode: UnapprovedPathMode::default(),
}
}
}
@ -351,6 +372,7 @@ impl Plugin for AssetPlugin {
AssetServerMode::Unprocessed,
self.meta_check.clone(),
watch,
self.unapproved_path_mode.clone(),
));
}
AssetMode::Processed => {
@ -367,6 +389,7 @@ impl Plugin for AssetPlugin {
AssetServerMode::Processed,
AssetMetaCheck::Always,
watch,
self.unapproved_path_mode.clone(),
))
.insert_resource(processor)
.add_systems(bevy_app::Startup, AssetProcessor::start);
@ -380,6 +403,7 @@ impl Plugin for AssetPlugin {
AssetServerMode::Processed,
AssetMetaCheck::Always,
watch,
self.unapproved_path_mode.clone(),
));
}
}
@ -390,7 +414,10 @@ impl Plugin for AssetPlugin {
.init_asset::<LoadedUntypedAsset>()
.init_asset::<()>()
.add_event::<UntypedAssetLoadFailedEvent>()
.configure_sets(PreUpdate, TrackAssets.after(handle_internal_asset_events))
.configure_sets(
PreUpdate,
AssetTrackingSystems.after(handle_internal_asset_events),
)
// `handle_internal_asset_events` requires the use of `&mut World`,
// and as a result has ambiguous system ordering with all other systems in `PreUpdate`.
// This is virtually never a real problem: asset loading is async and so anything that interacts directly with it
@ -587,9 +614,12 @@ impl AssetApp for App {
PostUpdate,
Assets::<A>::asset_events
.run_if(Assets::<A>::asset_events_condition)
.in_set(AssetEvents),
.in_set(AssetEventSystems),
)
.add_systems(
PreUpdate,
Assets::<A>::track_assets.in_set(AssetTrackingSystems),
)
.add_systems(PreUpdate, Assets::<A>::track_assets.in_set(TrackAssets))
}
fn register_asset_reflect<A>(&mut self) -> &mut Self
@ -619,13 +649,21 @@ impl AssetApp for App {
/// A system set that holds all "track asset" operations.
#[derive(SystemSet, Hash, Debug, PartialEq, Eq, Clone)]
pub struct TrackAssets;
pub struct AssetTrackingSystems;
/// Deprecated alias for [`AssetTrackingSystems`].
#[deprecated(since = "0.17.0", note = "Renamed to `AssetTrackingSystems`.")]
pub type TrackAssets = AssetTrackingSystems;
/// A system set where events accumulated in [`Assets`] are applied to the [`AssetEvent`] [`Events`] resource.
///
/// [`Events`]: bevy_ecs::event::Events
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
pub struct AssetEvents;
pub struct AssetEventSystems;
/// Deprecated alias for [`AssetEventSystems`].
#[deprecated(since = "0.17.0", note = "Renamed to `AssetEventSystems`.")]
pub type AssetEvents = AssetEventSystems;
#[cfg(test)]
mod tests {
@ -639,7 +677,7 @@ mod tests {
},
loader::{AssetLoader, LoadContext},
Asset, AssetApp, AssetEvent, AssetId, AssetLoadError, AssetLoadFailedEvent, AssetPath,
AssetPlugin, AssetServer, Assets, DuplicateLabelAssetError, LoadState,
AssetPlugin, AssetServer, Assets, LoadState, UnapprovedPathMode,
};
use alloc::{
boxed::Box,
@ -655,8 +693,7 @@ mod tests {
prelude::*,
schedule::{LogLevel, ScheduleBuildSettings},
};
use bevy_log::LogPlugin;
use bevy_platform_support::collections::HashMap;
use bevy_platform::collections::HashMap;
use bevy_reflect::TypePath;
use core::time::Duration;
use serde::{Deserialize, Serialize};
@ -695,8 +732,6 @@ mod tests {
CannotLoadDependency { dependency: AssetPath<'static> },
#[error("A RON error occurred during loading")]
RonSpannedError(#[from] ron::error::SpannedError),
#[error(transparent)]
DuplicateLabelAssetError(#[from] DuplicateLabelAssetError),
#[error("An IO error occurred during loading")]
Io(#[from] std::io::Error),
}
@ -727,7 +762,7 @@ mod tests {
.map_err(|_| Self::Error::CannotLoadDependency {
dependency: dep.into(),
})?;
let cool = loaded.get_asset().get();
let cool = loaded.get();
embedded.push_str(&cool.text);
}
Ok(CoolText {
@ -742,7 +777,7 @@ mod tests {
.sub_texts
.drain(..)
.map(|text| load_context.add_labeled_asset(text.clone(), SubText { text }))
.collect::<Result<Vec<_>, _>>()?,
.collect(),
})
}
@ -826,11 +861,7 @@ mod tests {
AssetSourceId::Default,
AssetSource::build().with_reader(move || Box::new(gated_memory_reader.clone())),
)
.add_plugins((
TaskPoolPlugin::default(),
LogPlugin::default(),
AssetPlugin::default(),
));
.add_plugins((TaskPoolPlugin::default(), AssetPlugin::default()));
(app, gate_opener)
}
@ -1728,11 +1759,7 @@ mod tests {
"unstable",
AssetSource::build().with_reader(move || Box::new(unstable_reader.clone())),
)
.add_plugins((
TaskPoolPlugin::default(),
LogPlugin::default(),
AssetPlugin::default(),
))
.add_plugins((TaskPoolPlugin::default(), AssetPlugin::default()))
.init_asset::<CoolText>()
.register_asset_loader(CoolTextLoader)
.init_resource::<ErrorTracker>()
@ -1780,49 +1807,6 @@ mod tests {
app.world_mut().run_schedule(Update);
}
#[test]
fn fails_to_load_for_duplicate_subasset_labels() {
let mut app = App::new();
let dir = Dir::default();
dir.insert_asset_text(
Path::new("a.ron"),
r#"(
text: "b",
dependencies: [],
embedded_dependencies: [],
sub_texts: ["A", "A"],
)"#,
);
app.register_asset_source(
AssetSourceId::Default,
AssetSource::build()
.with_reader(move || Box::new(MemoryAssetReader { root: dir.clone() })),
)
.add_plugins((
TaskPoolPlugin::default(),
LogPlugin::default(),
AssetPlugin::default(),
));
app.init_asset::<CoolText>()
.init_asset::<SubText>()
.register_asset_loader(CoolTextLoader);
let asset_server = app.world().resource::<AssetServer>().clone();
let handle = asset_server.load::<CoolText>("a.ron");
run_app_until(&mut app, |_world| match asset_server.load_state(&handle) {
LoadState::Loading => None,
LoadState::Failed(err) => {
assert!(matches!(*err, AssetLoadError::AssetLoaderError(_)));
Some(())
}
state => panic!("Unexpected asset state: {state:?}"),
});
}
// This test is not checking a requirement, but documenting a current limitation. We simply are
// not capable of loading subassets when doing nested immediate loads.
#[test]
@ -1846,11 +1830,7 @@ mod tests {
AssetSource::build()
.with_reader(move || Box::new(MemoryAssetReader { root: dir.clone() })),
)
.add_plugins((
TaskPoolPlugin::default(),
LogPlugin::default(),
AssetPlugin::default(),
));
.add_plugins((TaskPoolPlugin::default(), AssetPlugin::default()));
app.init_asset::<CoolText>()
.init_asset::<SubText>()
@ -1933,4 +1913,91 @@ mod tests {
#[derive(Asset, TypePath)]
pub struct TupleTestAsset(#[dependency] Handle<TestAsset>);
fn unapproved_path_setup(mode: UnapprovedPathMode) -> App {
let dir = Dir::default();
let a_path = "../a.cool.ron";
let a_ron = r#"
(
text: "a",
dependencies: [],
embedded_dependencies: [],
sub_texts: [],
)"#;
dir.insert_asset_text(Path::new(a_path), a_ron);
let mut app = App::new();
let memory_reader = MemoryAssetReader { root: dir };
app.register_asset_source(
AssetSourceId::Default,
AssetSource::build().with_reader(move || Box::new(memory_reader.clone())),
)
.add_plugins((
TaskPoolPlugin::default(),
AssetPlugin {
unapproved_path_mode: mode,
..Default::default()
},
));
app.init_asset::<CoolText>();
app
}
fn load_a_asset(assets: Res<AssetServer>) {
let a = assets.load::<CoolText>("../a.cool.ron");
if a == Handle::default() {
panic!()
}
}
fn load_a_asset_override(assets: Res<AssetServer>) {
let a = assets.load_override::<CoolText>("../a.cool.ron");
if a == Handle::default() {
panic!()
}
}
#[test]
#[should_panic]
fn unapproved_path_forbid_should_panic() {
let mut app = unapproved_path_setup(UnapprovedPathMode::Forbid);
fn uses_assets(_asset: ResMut<Assets<CoolText>>) {}
app.add_systems(Update, (uses_assets, load_a_asset_override));
app.world_mut().run_schedule(Update);
}
#[test]
#[should_panic]
fn unapproved_path_deny_should_panic() {
let mut app = unapproved_path_setup(UnapprovedPathMode::Deny);
fn uses_assets(_asset: ResMut<Assets<CoolText>>) {}
app.add_systems(Update, (uses_assets, load_a_asset));
app.world_mut().run_schedule(Update);
}
#[test]
fn unapproved_path_deny_should_finish() {
let mut app = unapproved_path_setup(UnapprovedPathMode::Deny);
fn uses_assets(_asset: ResMut<Assets<CoolText>>) {}
app.add_systems(Update, (uses_assets, load_a_asset_override));
app.world_mut().run_schedule(Update);
}
#[test]
fn unapproved_path_allow_should_finish() {
let mut app = unapproved_path_setup(UnapprovedPathMode::Allow);
fn uses_assets(_asset: ResMut<Assets<CoolText>>) {}
app.add_systems(Update, (uses_assets, load_a_asset));
app.world_mut().run_schedule(Update);
}
}

Some files were not shown because too many files have changed in this diff Show More