# 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>
# Objective
Fixes#16531
I also added change detection when creating the pipeline, which
technically isn't needed but it felt weird leaving it as is.
## Solution
Remove the pipeline if CAS is disabled. The uniform was already being
removed, which caused flickering / weirdness.
## Testing
Tested the anti_alias example by toggling CAS a bunch on/off.
# Objective
Animating component fields requires too much boilerplate at the moment:
```rust
#[derive(Reflect)]
struct FontSizeProperty;
impl AnimatableProperty for FontSizeProperty {
type Component = TextFont;
type Property = f32;
fn get_mut(component: &mut Self::Component) -> Option<&mut Self::Property> {
Some(&mut component.font_size)
}
}
animation_clip.add_curve_to_target(
animation_target_id,
AnimatableKeyframeCurve::new(
[0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]
.into_iter()
.zip([24.0, 80.0, 24.0, 80.0, 24.0, 80.0, 24.0]),
)
.map(AnimatableCurve::<FontSizeProperty, _>::from_curve)
.expect("should be able to build translation curve because we pass in valid samples"),
);
```
## Solution
This adds `AnimatedField` and an `animated_field!` macro, enabling the
following:
```rust
animation_clip.add_curve_to_target(
animation_target_id,
AnimatableCurve::new(
animated_field!(TextFont::font_size),
AnimatableKeyframeCurve::new(
[0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]
.into_iter()
.zip([24.0, 80.0, 24.0, 80.0, 24.0, 80.0, 24.0]),
)
.expect(
"should be able to build translation curve because we pass in valid samples",
),
),
);
```
This required reworking the internals a bit, namely stripping out a lot
of the `Reflect` usage, as that implementation was fundamentally
incompatible with the `AnimatedField` pattern. `Reflect` was being used
in this context just to downcast traits. But we can get downcasting
behavior without the `Reflect` requirement by implementing `Downcast`
for `AnimationCurveEvaluator`.
This also reworks "evaluator identity" to support either a (Component /
Field) pair, or a TypeId. This allows properties to reuse evaluators,
even if they have different accessor methods. The "contract" here is
that for a given (Component / Field) pair, the accessor will return the
same value. Fields are identified by their Reflect-ed field index. The
(TypeId, usize) is prehashed and cached to optimize for lookup speed.
This removes the built-in hard-coded TranslationCurve / RotationCurve /
ScaleCurve in favor of AnimatableField.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
The documentation for `bevy_app::App.world()` (and its mut variant)
could confuse some into thinking that this is the only World that the
App will contain.
## Solution
Clarify the documentation for `bevy_app::App.world()` (and its mut
variant), to say that it returns the main subapp's world. This helps
imply that Apps can contain more than one world (albeit, only one per
SubApp).
## Testing
This is a documentation change, with no changes to doctests. Thus,
testing is not necessary beyond ensuring the link syntax is correct.
# Objective
Fixes#16521
## Solution
If an empty span is encountered (such as the default `Text` value), we
skip it entirely when updating buffers. This prevents unnecessarily
bailing when the font doesn't exist (ex: when the default font is
disabled)
# Objective
- Fixes#16363
- Ensure that someone using minimum version doesn't get the bugs that
were fixed in the 23.0.1 patch
## Solution
- Use wgpu 23.0.1
# Objective
In the [*Similar parameters* section of
`Query`](https://dev-docs.bevyengine.org/bevy/ecs/prelude/struct.Query.html#similar-parameters),
the doc link for `Single` actually links to `Query::single`, and
`Option<Single>` just links to `Option`. They should both link to
`Single`!
The first link is broken because there is a reference-style link defined
for `single`, but not for `Single`, and rustdoc treats the link as
case-insensitive for some reason.
## Solution
Fix the links!
## Testing
I built the docs locally with `cargo doc` and tested the links.
# Objective
`TemporaryRenderEntity` currently uses `SparseSet` storage, but doesn't
seem to fit the criteria for a component that would benefit from this.
Typical usage of `TemporaryRenderEntity` (and all current usages of it
in engine as far as I can tell) would be to spawn an entity with it once
and then iterate over it once to despawn that entity.
`SparseSet` is said to be useful for insert/removal perf at the cost of
iteration perf.
## Solution
Use the default table storage
## Testing
Possibly this could show up in stress tests like `many_buttons`. I
didn't do any benchmarking.
# Objective
When using a rect for a ui image, its content size is still equal to the
size of the full image instead of the size of the rect.
## Solution
Use the rect size if it is present.
## Testing
I tested it using all 4 possible combinations of having a rect and
texture atlas or not. See the showcase section.
---
## Showcase
<details>
<summary>Click to view showcase</summary>
```rust
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
.add_systems(Startup, create_ui)
.run();
}
fn create_ui(
mut commands: Commands,
assets: Res<AssetServer>,
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
mut ui_scale: ResMut<UiScale>,
) {
let texture = assets.load("textures/fantasy_ui_borders/numbered_slices.png");
let layout = TextureAtlasLayout::from_grid(UVec2::splat(16), 3, 3, None, None);
let texture_atlas_layout = texture_atlas_layouts.add(layout);
ui_scale.0 = 2.;
commands.spawn(Camera2d::default());
commands
.spawn(Node {
display: Display::Flex,
align_items: AlignItems::Center,
..default()
})
.with_children(|parent| {
// nothing
parent.spawn(ImageNode::new(texture.clone()));
// with rect
parent.spawn(ImageNode::new(texture.clone()).with_rect(Rect::new(0., 0., 16., 16.)));
// with rect and texture atlas
parent.spawn(
ImageNode::from_atlas_image(
texture.clone(),
TextureAtlas {
layout: texture_atlas_layout.clone(),
index: 1,
},
)
.with_rect(Rect::new(0., 0., 8., 8.)),
);
// with texture atlas
parent.spawn(ImageNode::from_atlas_image(
texture.clone(),
TextureAtlas {
layout: texture_atlas_layout.clone(),
index: 2,
},
));
});
}
```
Before this change:
<img width="529" alt="Screenshot 2024-11-21 at 11 55 45"
src="https://github.com/user-attachments/assets/23196003-08ca-4049-8409-fe349bd5aa54">
After the change:
<img width="400" alt="Screenshot 2024-11-21 at 11 54 54"
src="https://github.com/user-attachments/assets/e2cd6ebf-859c-40a1-9fc4-43bb28b024e5">
</details>
# Objective
Original motivation was a bundle I am migrating that is `Copy` which
needs to be synced to the render world. It probably doesn't actually
*need* to be `Copy`, so this isn't critical or anything.
I am continuing to use this bundle while bundles still exist to give
users an easier migration path.
## Solution
These ZSTs might as well be `Copy`. Add `Copy` derives.
PR #15164 made Bevy consider the center of the mesh to be the center of
the axis-aligned bounding box (AABB). Unfortunately, this breaks
crossfading in many cases. LODs may have different AABBs and so the
center of the AABB may differ for different LODs of the same mesh. The
crossfading, however, relies on all LODs having *precisely* the same
position.
To address this problem, this PR adds a new field, `use_aabb`, to
`VisibilityRange`, which makes the AABB center point behavior opt-in.
@BenjaminBrienen first noticed this issue when reviewing PR #16286. That
PR contains a video showing the effects of this regression on the
`visibility_range` example. This commit fixes that example.
## Migration Guide
* The `VisibilityRange` component now has an extra field, `use_aabb`.
Generally, you can safely set it to false.
We have an early-out to avoid updating `RenderVisibilityRanges` when a
`VisibilityRange` component is *modified*, but not when one is
*removed*. This means that removing `VisibilityRange` from an entity
might not update the rendering.
This PR fixes the issue by adding a check for removed
`VisibilityRange`s.
# Objective
- Fixes#16472.
## Solution
- Add flags to `SpritePlugin` and `UiPlugin` to disable their picking
backends.
## Testing
- The change is pretty trivial, so not much to test!
---
## Migration Guide
- `UiPlugin` now contains an extra `add_picking` field if
`bevy_ui_picking_backend` is enabled.
- `SpritePlugin` is no longer a unit struct, and has one field if
`bevy_sprite_picking_backend` is enabled (otherwise no fields).