A backport of https://github.com/bevyengine/bevy/pull/18840 to version
0.15.3. I was unsure whether I should also update the versions of all
crates. Looking at previous backports this was not done.
The code which relies on the old solver behavior was introduced in
https://github.com/bevyengine/bevy/pull/15184 which is from version
0.15. So this is the only version which needs a backport.
cc @mockersf
Fixes#17945
Check if the view being extracted has OIT enabled and incorporate the
associated bit into the mesh pipeline key.
I basically have no idea what's going on in the renderer, so let me know
if I missed something, which is extraordinarily possible.
I modified the `order_independent_transparency` example to put
everything on the default render layer and render a gizmo at the origin.
Previously, this would cause the application to panic.
# Objective
Fixes#17488
## Solution
The world update logic happened in the the `about_to_wait` winit window
callback, but this is is not correct as (1) the winit documentation
states that the callback should not be used for that purpose and (2) the
callback is not fired when the window is resized or being dragged.
However, that callback was used in #11245 to fix an iOS bug (which
caused the regression). The solution presented here is a workaround
until the event loop code can be re-written.
## Testing
I confirmed that the `eased_motion` example continued to be animated
when dragging or resizing the window.
https://github.com/user-attachments/assets/ffaf0abf-4cd7-479b-83e9-e1850aaf3513
# Objective
Fixes#16628
## Solution
Matrices were being applied in the wrong order.
## Testing
Ran `skybox` example with rotations applied to the `Skybox` on the `x`,
`y`, and `z` axis (one at a time).
e.g.
```rust
Skybox {
image: skybox_handle.clone(),
brightness: 1000.0,
rotation: Quat::from_rotation_y(-45.0_f32.to_radians()),
}
```
## Showcase
[Screencast_20250121_151232.webm](https://github.com/user-attachments/assets/3df68714-f5f1-4d8c-8e08-cbab525a8bda)
# Objective
This allows for the usage of the MouseScrollUnit as a key to a HashSet
and HashMap. I have a need for this, but this basic functionality is
currently missing.
## Solution
Add the derive Hash attribute to the MouseScrollUnit type.
## Testing
- Did you test these changes? If so, how?
No, but I did perform a `cargo build`. My laptop is failing to run
`cargo test` without crashing.
- Are there any parts that need more testing?
If someone could run a `cargo test` for completeness, that would be
great but this is a trivial change.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
They simply need to ensure that the common Hash derive macro works as
expected for the basic MouseScrollUnit type.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
Ubuntu 22.04
# Objective
Fixes#14970
## Solution
It seems the clamp call in `ui.wgsl` had the parameters order incorrect.
## Testing
Tested using examples/ui in native and my current project in wasm - both
in linux.
Could use some help with testing in other platforms.
---
I noticed that this component was not being returned correctly by the
`bevy_remote` api
```json
"errors": {
"bevy_picking::focus::PickingInteraction": {
"code": -23402,
"message": "Unknown component type: `bevy_picking::focus::PickingInteraction`"
}
}
```
Fixes#16783
Works around a `cosmic-text` bug or limitation by triggering a re-layout
with the calculated width from the first layout run. See linked issue.
Credit to @ickshonpe for the clever solution.
This has a significant performance impact only on unbounded text that
are not `JustifyText::Left`, which is still a bit of a bummer because
text2d performance in 0.15.1 is already not great. But this seems better
than alignment not working.
||many_text2d nfc re|many_text2d nfc re center|
|-|-|-|
|unbounded-layout-no-fix|3.06|3.10|
|unbounded-layout-fix|3.05 ⬜ -0.2%|2.71 🟥 -12.5%|
I added a centered text to the `text2d` example.
`cargo run --example text2d`
We should look at other text examples and stress tests. I haven't tested
as thoroughly as I would like, so help testing that this doesn't break
something in UI would be appreciated.
# Objective
The original fix (bevyengine/bevy#11870) did not actually implement the
described logic. It checked if there were independently multiple loaders
for a given asset type and multiple loaders for a given extension.
However, this did not handle the case where those loaders were not the
same. For example, there could be a loader for type `Foo` and extension
`.foo`. Anther loader could exist for type `Bar` but extension `.bar`.
If a third loader was added for type `Foo` but extension `.bar`, the
warning would have been incorrectly logged.
## Solution
Instead of independently checking to see if there are preexisting
loaders for both the extension and type, look up the indices of the
loaders for the type in question. Then check to see if the loaders
registered for the extensions has any overlap. Only log if there are
loaders that fit this criteria.
## Testing
Ran CI tests. Locally tested the situation describe in the objective
section for the normal `App::init_asset_loader` flow. I think testing
could be done on the pre-registration flow for loaders still. I tested
on Windows, but the changes should not be affected by platform.
# Objective
- As stated in the linked issue, if a Mesh2D is drawn with elements with
a positive Z value, resulting gizmos get drawn behind instead of in
front of them.
- Fixes#17053
## Solution
- Similar to the change done for the `SpritePipeline` in the relevant
commit (5abc32ceda), this PR changes both
line gizmos to avoid writing to the depth buffer and always pass the
depth test to ensure they are not filtered out.
## Testing
- Tested with the provided snippet in #17053
- I looked over the `2d_gizmos` example, but it seemed like adding more
elements there to demonstrate this might not be the best idea? Looking
for guidance here on if that should be updated or if a new gizmo example
needs to be made.
# Objective
Fix alignment calculations in our rendering code.
Fixes#16992
The `gpu_readback::align_byte_size` function incorrectly rounds aligned
values to the next alignment.
If we assume the alignment to be 256 (because that's what wgpu says it
its) the function would align 0 to 256, 256 to 512, etc...
## Solution
Forward the `gpu_readback::align_byte_size` to
`RenderDevice::align_copy_bytes_per_row` so we don't implement the same
method twice.
Simplify `RenderDevice::align_copy_bytes_per_row`.
## Testing
Ran the code provided in #16992 to see if the issue has been solved +
added a test to check if `align_copy_bytes_per_row` returns the correct
values.
After a recent fix for a panic in the pbr example (#16976), the code
contains the following comment:
```rust
// This system relies on system parameters that are not available at start
// Ignore parameter failures so that it will run when possible
.add_systems(Update, environment_map_load_finish.never_param_warn())
```
However, this explanation is incorrect. `EnvironmentMapLabel` is
available at start. The real issue is that it is no longer available
once it has been removed by `environment_map_load_finish`.
- Remove confusing/incorrect comment and `never_param_warn()`.
- Make `Single<Entity, With<EnvironmentMapLabel>>` optional in
`environment_map_load_finish`, and check that the entity has not yet
been despawned.
Since it is expected that an entity is no longer there once it has been
despawned, it seems better to me to handle this case in
`environment_map_load_finish`.
Ran `cargo run --example pbr`.
# Objective
Fixes#16879
## Solution
Moved the construction of the root path of the assets folder out of
`FileWatcher::new()` and into `source.rs`, as the path is checked there
with `path.exists()` and fails in certain configurations eg., virtual
workspaces.
## Testing
Applied fix to a private fork and tested against both standard project
setups and virtual workspaces. Works without issue on both. Have tested
under macOS and Arch Linux.
---------
Co-authored-by: JP Stringham <jp@bloomdigital.to>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- Fixes#16959
- The `pbr.rs` example in the 3d section panicked because of the changes
in #16638, that was not supposed to happen
## Solution
- For now it's sufficient to introduce a `never_param_warn` call when
adding the fallible system into the app
## Testing
- Tested on my machine via `cargo r --example pbr`, it built and ran
successfully
---------
Co-authored-by: Freya Pines <freya@Freyas-MacBook-Air.local>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
ensure that `animate_targets` runs **before**
`bevy_render::mesh::inherit_weights` to address the one-frame delay
Fixes#16554
## Solution
switch ordering constraints from `after` to `before`
## Testing
ran bevy_animation tests and the animated_fox example on MacOS
Fixes#16978
While testing, discovered that the morph weight interface in
`scene_viewer` has been broken for a while (panics when loaded model has
morph weights), probably since #15591. Fixed that too.
While testing, saw example text in morph interface with [wrong
padding](https://bevyengine.org/learn/contribute/helping-out/creating-examples/#visual-guidelines).
Fixed that too. Left the small font size because there may be a lot of
morphs to display, so that seems intentional.
Use normal queries and bail early
Morph interface can be tested with
```
cargo run --example scene_viewer assets/models/animated/MorphStressTest.gltf
```
I noticed that this fix is different than what is happening in #16976.
Feel free to discard this for an alternative fix. I opened this anyway
to document the issue with morph weight display.
This is on top of #16966 which is required to test.
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
# Objective
Fixes#16850
## Solution
Add a new function `SubApp::take_extract()`, similar to
`Option::take()`, which allows stealing the currently installed extract
function of a sub-app, with the intent to replace it with a custom one
calling the original one via `set_extract()`.
This pattern enables registering a custom "world sync" function similar
to the existing one `entity_sync_system()`, to run custom world sync
logic with mutable access to both the main and render worlds.
## Testing
`cargo r -p ci` currently doesn't build locally, event after upgrading
rustc to latest and doing a `cargo update`.
# Objective
- Fixes#16571
## Solution
- When position delta is zero, don't trigger `Drag` or `DragOver` events
## Testing
- tested with the code from the issue
# Objective
- Fixes#16563
- Make sure bevy_image is available when needed
## Solution
- Add a new feature for `bevy_image`
- Also enable the `bevy_image` feature in `bevy_internal` for all
features that use `bevy_image` themselves
This PR simply exposes Bevy PBR's
`TONEMAPPING_LUT_TEXTURE_BINDING_INDEX` and
`TONEMAPPING_LUT_SAMPLER_BINDING_INDEX`.
# Objective
Alongside #16932, this is the last required change to be able to replace
Bevy's built-in deferred lighting pass with a custom one based on the
original logic.
Fixes a crash when using deferred rendering but disabling the default
deferred lighting plugin.
# The Issue
The `ScreenSpaceReflectionsPlugin` references
`NodePbr::DeferredLightingPass`, which hasn't been added when
`PbrPlugin::add_default_deferred_lighting_plugin` is `false`.
This yields the following crash:
```
thread 'main' panicked at /Users/marius/Documents/dev/bevy/crates/bevy_render/src/render_graph/graph.rs:155:26:
InvalidNode(DeferredLightingPass)
stack backtrace:
0: rust_begin_unwind
at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/std/src/panicking.rs:665:5
1: core::panicking::panic_fmt
at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/core/src/panicking.rs:74:14
2: bevy_render::render_graph::graph::RenderGraph::add_node_edges
at /Users/marius/Documents/dev/bevy/crates/bevy_render/src/render_graph/graph.rs:155:26
3: <bevy_app::sub_app::SubApp as bevy_render::render_graph::app::RenderGraphApp>::add_render_graph_edges
at /Users/marius/Documents/dev/bevy/crates/bevy_render/src/render_graph/app.rs:66:13
4: <bevy_pbr::ssr::ScreenSpaceReflectionsPlugin as bevy_app::plugin::Plugin>::finish
at /Users/marius/Documents/dev/bevy/crates/bevy_pbr/src/ssr/mod.rs:234:9
5: bevy_app::app::App::finish
at /Users/marius/Documents/dev/bevy/crates/bevy_app/src/app.rs:255:13
6: bevy_winit::state::winit_runner
at /Users/marius/Documents/dev/bevy/crates/bevy_winit/src/state.rs:859:9
7: core::ops::function::FnOnce::call_once
at /Users/marius/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
8: core::ops::function::FnOnce::call_once{{vtable.shim}}
at /Users/marius/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
9: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
at /Users/marius/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/boxed.rs:2454:9
10: bevy_app::app::App::run
at /Users/marius/Documents/dev/bevy/crates/bevy_app/src/app.rs:184:9
11: bevy_deferred_test::main
at ./src/main.rs:9:5
12: core::ops::function::FnOnce::call_once
at /Users/marius/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
```
### Minimal reproduction example:
```rust
use bevy::core_pipeline::prepass::{DeferredPrepass, DepthPrepass};
use bevy::pbr::{DefaultOpaqueRendererMethod, PbrPlugin, ScreenSpaceReflections};
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(PbrPlugin {
add_default_deferred_lighting_plugin: false,
..default()
}))
.add_systems(Startup, setup)
.insert_resource(DefaultOpaqueRendererMethod::deferred())
.run();
}
/// set up a camera
fn setup(
mut commands: Commands
) {
// camera
commands.spawn((
Camera3d::default(),
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
DepthPrepass,
DeferredPrepass,
ScreenSpaceReflections::default(),
));
}
```
# The Fix
When no node under the default lighting node's label exists, this label
isn't added to the SSR's graph node edges. It's good to keep the
SSRPlugin enabled, this way, users can plug in their own lighting
system, which I have successfully done on top of this PR.
# Workarounds
A current workaround for this issue is to re-use Bevy's
`NodePbr::DeferredLightingPass` as the label for your own custom
lighting pass node.
Fixes: #16578
This is a patch fix, proper fix requires a breaking change.
Added `Panic` enum variant and using is as the system meta default.
Warn once behavior can be enabled same way disabling panic (originally
disabling wans) is.
To fix an issue with the current architecture, where **all** combinator
system params get checked together,
combinator systems only check params of the first system.
This will result in old, panicking behavior on subsequent systems and
will be fixed in 0.16.
Ran unit tests and `fallible_params` example.
---------
Co-authored-by: François Mockers <mockersf@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Scroll position uses physical coordinates. This means scrolling may go
faster or slower depending on the scroll factor. Also the scrolled
position will change when the scale factor changes.
## Solution
In `ui_layout_system` convert `max_possible_offset` to logical
coordinates before clamping the scroll position. Then convert the
clamped scroll position to physical coordinates before propagating it to
the node's children.
## Testing
Look at the `scroll` example. On main if you change your display's scale
factor the items displayed by the scrolling lists will change because
`ScrollPosition`'s displacement values don't respect scale factor. With
this PR the displacement will be scaled too, and the won't move.
Instead of clipping the non-visable sections of box-shadows, the shadow
is scaled to fit into the remaining area after clipping because the
normalized coordinates that are meant to border the unclipped subsection
of the shadow are always set to `[Vec2::ZERO, Vec2::X, Vec2::ONE,
Vec2::Y]`,
Calculate the coordinates for the corners of the visible area.
Test app:
```rust
use bevy::color::palettes::css::RED;
use bevy::color::palettes::css::WHITE;
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
commands
.spawn(Node {
..Default::default()
})
.with_children(|commands| {
commands
.spawn((
Node {
width: Val::Px(100.),
height: Val::Px(100.),
margin: UiRect {
left: Val::Px(100.),
top: Val::Px(300.),
..Default::default()
},
overflow: Overflow::clip(),
..Default::default()
},
BackgroundColor(WHITE.into()),
))
.with_children(|commands| {
commands.spawn((
Node {
position_type: PositionType::Absolute,
left: Val::Px(50.),
top: Val::Px(50.),
width: Val::Px(100.),
height: Val::Px(100.),
..Default::default()
},
BackgroundColor(RED.into()),
BoxShadow::from(ShadowStyle {
x_offset: Val::ZERO,
y_offset: Val::ZERO,
spread_radius: Val::Px(50.),
blur_radius: Val::Px(6.),
..Default::default()
}),
));
});
});
}
```
Main:
<img width="103" alt="bad_shadow"
src="https://github.com/user-attachments/assets/6f7ade0e-959f-4d18-92e8-903630eb8cd3"
/>
This PR:
<img width="98" alt="clipped_shadow"
src="https://github.com/user-attachments/assets/7f576c94-908c-4fe6-abaa-f18fefe05207"
/>
# Objective
Fixes#16771
## Solution
Fixed typo in code.
## Testing
- Did you test these changes? If so, how?
I tested on my own example, that I included in the issue. It was
behaving as I expected.
Here is the screenshot after fix, the screenshot before the fix can be
found in the issue.

# Objective
The parameter names for `bevy::math::ops::atan2` are labelled such that
`x` is the first argument and `y` is the second argument, but it passes
those arguments directly to
[`f32::atan2`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.atan2),
whose parameters are expected to be `(y, x)`. This PR changes the
parameter names in the bevy documentation to use the correct order for
the operation being performed. You can verify this by doing:
```rust
fn main() {
let x = 3.0;
let y = 4.0;
let angle = bevy::math::ops::atan2(x, y);
// standard polar coordinates formula
dbg!(5.0 * angle.cos(), 5.0 * angle.sin());
}
```
This will print `(4.0, 3.0)`, which has flipped `x` and `y`. The problem
is that the `atan2` function to calculate the angle was really expecting
`(y, x)`, not `(x, y)`.
## Solution
I flipped the parameter names for `bevy::math::ops::atan2` and updated
the documentation. I also removed references to `self` and `other` from
the documentation which seemed to be copied from the `f32::atan2`
documentation.
## Testing
Not really needed, you can compare the `f32::atan2` docs to the
`bevy::math::ops::atan2` docs to see the problem is obvious. If a test
is required I could add a short one.
## Migration Guide
I'm not sure if this counts as a breaking change, since the
implementation clearly meant to use `f32::atan2` directly, so it was
really just the parameter names that were wrong.
# Objective
- This fixes raycast picking with lyon
- reverse winding of 2D meshes currently results in them being rendered
but not pickable as the raycast passes through the backface and would
only hit "from below"
## Solution
- Disables backface culling for Mesh2d
## Testing
- Tested picking with bevy_prototype_lyon
- Could probably use testing with Mesh3d (should not be affected) and
SimplifiedMesh (no experience with that, could have the same issue if
used for 2D?)
---------
Co-authored-by: Aevyrie <aevyrie@gmail.com>
# Objective
Outside of the `bevy_ecs` crate it's hard to implement `SystemParam`
trait on params that require access to the `World`, because `init_state`
expects user to extend access in `SystemMeta` and access-related fields
of `SystemMeta` are private.
## Solution
Expose those fields as a functions
# Objective
BrpQueryRow doesn't serialize `has` field if it is empty. That is okay
until you try to deserialize it after. Then it will fail to deserialize
due to missing field.
## Solution
Serde support using default value when field is missing, this PR adds
that.
# Objective
Fix a [Blenvy](https://github.com/kaosat-dev/Blenvy) crash due to a
missing type registration for `TextEntity` (as the type is used by
`ComputedTextBlock` but wasn't itself registered.)
## Solution
- Added the missing type registration
## Testing
- N/A
# Objective
- Fixes#16594
## Solution
- `StateTransitionSteps` was made public
## Testing
- _Did you test these changes? If so, how?_
I am now able to import it and use it.
---
## Showcase
- Users can now write their own state scoped resources
```rust
fn init_state_scoped_resource<R: Resource + FromWorld>(
&mut self,
state: impl States,
) -> &mut Self {
use bevy::state::state::StateTransitionSteps; // this PR in action
self.add_systems(
StateTransition,
(
clear_state_scoped_resource_impl::<_, R>(state.clone())
.in_set(StateTransitionSteps::ExitSchedules), // and here
init_state_scoped_resource_impl::<_, R>(state)
.in_set(StateTransitionSteps::EnterSchedules), // here too
),
);
self
}
```
# Objective
See title.
## Solution
Move `bevy_animation` import to where it is used.
## Testing
Compiled with and without `bevy_animation` feature enabled.
# Objective
- Avoid recreating the monitor every loop (temp fix until it's done
properly on winit side)
- Add a new `WinitSettings` preset for mobile that makes the winit loop
wait more and recommend its usage
Co-authored by: @BenjaminBrienen
# Objective
Fixes#16494. Closes#16539, which this replaces. Suggestions alone
weren't enough, so now we have a new PR!
---------
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
# Objective
- In 0.14, ZIndex and GlobalZIndex where split from a shared enum into
separate components. There have been a few people confused by the
behavior of ZIndex when they really needed GlobalZIndex.
## Solution
- Update ZIndex docs to improve discoverability of GlobalZIndex.
---------
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>