# Objective
- Progress towards #19887.
## Solution
- Convert FromWorld implementations into systems.
- Move any resource "manipulation" from `Plugin::finish` to systems.
- Add `after` dependencies to any uses of these resources (basically all
`SpritePipeline`).
## Testing
- Ran the `sprite`, and `mesh2d_manual` example and it worked.
# Objective
- Part of #20115
We want to encapsulate each part of `ScheduleGraph` into its own
specific struct to make parts of it easier to reuse and maintain.
## Solution
- Pulled `ScheduleGraph::systems` and `ScheduleGraph::system_conditions`
into a `Systems` struct and added a field for this new struct to
`ScheduleGraph`
- Broke up `ScheduleGraph::uninit` into `Systems::uninit` and
`SystemSets::uninit` to eliminate `ScheduleGraph`'s direct field access
of these types
- Removed node and condition accessors from `ScheduleGraph`; the same
operations are now available on `Systems` and `SystemSets` instead
(accessible via their `pub` fields on `ScheduleGraph`)
- Moved `Systems`, `SystemSets`, `SystemNode`, `SystemWithAccess`, and
`ConditionWithAccess` into a separate file.
## Testing
Added two new tests covering the API surface of `Systems` and
`SystemSets`, respectively.
---------
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
# Objective
- Improve usability of read-only systems.
## Solution
- Added `on_unimplemented` diagnostics for types/functions that aren't
read-only systems.
- Added `BoxedReadOnlySystem` type alias, similar to `BoxedSystem`.
## Testing
Can/should we test these diagnostics?
# Objective
Fix#18079
## Solution
- `EntityCloner` can now move components that don't have `Clone` or
`Reflect` implementation.
- Components with `ComponentCloneBehavior::Ignore` will not be moved.
- Components with `ComponentCloneBehavior::Custom` will be cloned using
their defined `ComponentCloneFn` and then removed from the source entity
to respect their `queue_deferred` logic.
- Relationships still need to be `Clone` or `Reflect` to be movable.
- Custom relationship data is now correctly preserved when cloned or
moved using `EntityCloner`.
## Testing
- Added new tests for moving components
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
The names of the variants of `InterpolationColorSpace` don't match the
corresponding `Color` variants, which could be potentially confusing.
For instance, `Color` has an `Oklaba` variant, in
`InterpolationColorSpace` it's called `OkLab`.
## Solution
Rename variants of `InterpolationColorSpace` to mirror the variants of
Color.
# Objective
Fixes https://github.com/bevyengine/bevy/issues/19535. Improve
interoperability of type documentation to make it easier for users to
find related types
## Solution
- Add a reference to
[`ComponentIdFor`](crate::component::ComponentIdFor) in the
`component_id` function documentation in
`crates/bevy_ecs/src/world/mod.rs`
- Add a reference to [`ComponentIdFor`](super::ComponentIdFor) in the
`component_id` function documentation in
`crates/bevy_ecs/src/component/info.rs`
## Testing
- Verify documentation generation: `cargo doc`
- Check the validity of cross-reference links in the documentation
- Confirm that the documentation generated by rustdoc can correctly jump
to the type definition of `ComponentIdFor`
# Objective
Three impls are generated for each of these traits when the
`reflect_functions` feature is enabled.
Helps with #19873.
## Solution
Two of the three (the `&T` and `&mut T` ones) can be avoided by instead
providing blanket impls. The impl for `T` remains.
## Testing
I checked the output via `cargo expand`.
According to `-Zmacro-stats`, the size of the `Reflect` code generate
for `bevy_ui` drops by 10.4%.
# Objective
- Clean up usage of BlitPipeline
## Solution
- add `create_bind_group` method
- adjust some field and variable names for clarity
## Testing
- ran 3d_scene
# Objective
Implement `Clone` for `IntoPipeSystem`, allowing for `T: IntoSystem +
Clone` patterns.
## Precedence
Same clone implementation/docs as `CombinatorSystem`
# Objective
> I think we should axe the shared `Event` trait entirely
It doesn't serve any functional purpose, and I don't think it's useful
pedagogically
@alice-i-cecile on discord
## Solution
- Remove `Event` as a supertrait of `BufferedEvent`
- Remove any `Event` derives that were made unnecessary
- Update release notes
---------
Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net>
# Objective
Fix lightmapped materials not respecting the
AmbientLight::affects_lightmapped_meshes setting.
NOTE: This only makes the setting work on the forward renderer. Making
it work on the deferred renderer would probably require encoding more
information in the g-buffer or similar. Please advise if I missed some
obvious way to get it working on deferred.
## Solution
- Make ambient light conditionally applied depending on the
affects_lightmapped_meshes setting when material mesh i lightmapped
- Remove what looks to be leftover `Lights` (wgsl) members:
`environment_map_smallest_specular_mip_level`,
`environment_map_intensity`. (These where not present in the rust
equivalent `GpuLights` and was not used in the wgsl code)
## Open Questions
- Ambient light is also blended into the transmitted light if
`DIFFUSE_TRANSMISSION` is enabled on the material. Should that also be
guarded by the same conditional as the indirect contribution?
## Testing
Ran a modified version of the lightmaps example, where there is a bright
red ambient light added and the small cube do not have a lightmap added
(To be able to see ambient light applied to meshes without lightmaps)
---
## Showcase
### Main: Lightmap example with bright red ambient, small box have no
lightmap
<img width="613" height="601" alt="Main"
src="https://github.com/user-attachments/assets/a3f206d7-5a1e-4590-8c40-69d5c6e06ce0"
/>
(All meshes get ambient light even when `affects_lightmapped_meshes =
false`)
### This PR: Lightmap example with bright red ambient, small box have no
lightmap
<img width="612" height="602" alt="With fix"
src="https://github.com/user-attachments/assets/d1a149a5-8994-4572-909f-8788ba2c38fc"
/>
(Only the small box get ambient light when `affects_lightmapped_meshes =
false`)
Fixes objects being lit by environment map light not having the
anisotropy effect applied correctly. The contribution of light from the
environment map with anisotropy is calculated but immediately discarded.
Instead what is applied is the regular environment map contribution
(calculated without anisotropy) that happen right after.
This patch fixes the logic here to what I think is the intended one.
Only calculate contribution once, with or without anisotropy depending
on shader specialization.
## Solution
- Properly apply normal modification if anisotropy is enabled.
- Remove duplicate environment map light calculation
## Testing
Tested this by running the anisotropy example and comparing main vs PR
results. See images below.
---
## Showcase
Main:
<img width="580" height="503" alt="Main-AnisoEnabled"
src="https://github.com/user-attachments/assets/47471a12-60cd-48ba-a32e-60086b6d162a"
/>
PR:
<img width="592" height="509" alt="WithFix-AnisoEnabled"
src="https://github.com/user-attachments/assets/e1f6b82c-1bac-40e1-8925-3abece05f406"
/>
</details>
# Objective
- So far only full buffer reads were supported. This adds the ability to
read a part of a buffer.
## Solution
- Allow passing in a start offset and a number of bytes to read when
creating the `Readback` component.
- I also removed the unused `src_start` and `dst_start` fields from
`ReadbackSource` as they were always 0.
## Testing
- Did you test these changes? If so, how?
I extended the example to also demonstrate partial reads.
- Are there any parts that need more testing?
Can't think of any.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
Run the `gpu_readback` example. It now also reads and prints a partially
read buffer.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
Only tested on Linux.
---
## Showcase
Example output:
<details>
<summary>Click to view showcase</summary>
```
2025-07-14T14:05:15.614876Z INFO gpu_readback: Buffer [257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272]
2025-07-14T14:05:15.614921Z INFO gpu_readback: Buffer range [261, 262, 263, 264, 265, 266, 267, 268]
2025-07-14T14:05:15.614937Z INFO gpu_readback: Image [257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
```
</details>
# Objective
- Fixes#19742
## Solution
- Initially I made it an optional feature on bevy_core_widgets and piped
it through to bevy_internal, but then I realized that its not actually
directly reference in the crate so its safe to just remove it.
# Objective
- Add a release note for the new zstd backend.
- Doing so, I realized it was really cumbersome to enable this feature
because we default-enable ruzstd AND make it take precedence if both are
enabled. We can improve ux a bit by making the optional feature take
precedence when both are enabled. This still doesnt remove the unneeded
dependency, but oh well.
Note: it would be nice to have a way to make zstd_c not do anything when
building wasm, but im not sure theres a way to do that, as it seems like
it would need negative features.
---------
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
# Objective
- Fix#20014
## Solution
- Don't make the `force_register_` family of functions always enqueue of
the component/resource; instead have them check again whether it was
already queued or not.
## Testing
- I added a small regression test but it's non-deterministic: if the bug
is fixed it will always pass, but if the bug is presen then it has a
(hopefully small) chance of passing. On my PC it failed after ~100
iterations, so hopefully 1000 is enough in CI (assuming it doesn't have
single core runners...)
---------
Co-authored-by: JaySpruce <jsprucebruce@gmail.com>
# Objective
- Progress towards #19887.
## Solution
- For cases that don't need to conditionally add systems, we can just
replace FromWorld impls with systems and then add those systems to
`RenderStartup`.
## Testing
- I ran the `lightmaps`, `reflection_probes`, `deferred_rendering`,
`volumetric_fog`, and `wireframe` examples.
# Objective
Follow on fixes for #19876, using `active_types` instead of
`active_fields` where appropriate.
Helps a tiny bit with #19873.
## Solution
- Describe the solution used to achieve the objective above.
## Testing
I checked the output of `cargo expand` and ran `cargo run -p ci`.
## Solution
While poking around `NestedLoader` I noticed that one code path fails to
pass on the loader's meta transform.
```diff
- .get_or_create_path_handle(path, None)
+ .get_or_create_path_handle(path, self.meta_transform)
```
This seems like a simple oversight - all other code paths pass on the
meta transform where possible - although I'm not familiar enough with
the asset system to be 100% sure. It was introduced when asset loaders
were changed to use the builder pattern (#13465).
Unfortunately I couldn't find an example that actually hits this code
path. So I don't have a good test case, and I don't know if any users
have experienced it as a bug.
## Testing
```
cargo test -p bevy_asset
```
Also tested various examples - `testbed_2d`, `testbed_3d` and everything
in `examples/asset`.
# Objective
Fix a crash when minimizing a window. (#16704) It happens when the
window contains Camera with a custom Viewport.
## Solution
Remove ExtractedCamera when the corresponding camera in main world has
zero target size. It indicates that the window is minimized.
## Testing
Tested in Windows 11.
Previously the split_screen example crashes when the window is
minimized; and with this change, it would not crash. Other behaviors
remain unchanged.
# Objective
- `bundle.rs` is also becoming quite big, let's split it
## Solution
- Split `bundle.rs` into the following:
- `bundle/mod.rs` with the main traits and their documentation
- `bundle/impls.rs` with the main trait implementations for components
and tuples
- `bundle/info.rs` with `BundleInfo` and other types for managing bundle
informations metadata
- `bundle/insert.rs` with code for inserting a bundle into an existing
entity
- `bundle/remove.rs` with code for removing a bundle from an existing
entity
- `bundle/spawner.rs` with code for inserting a bundle on a fresh entity
- `bundle/tests.rs` with code for tests
## Reviewer notes
I suggest reviewing with `--color-moved`, possibly commit-by-commit, in
order to quickly verify that most lines were only moved and not changed.
# Objective
- Extracted from https://github.com/bevyengine/bevy/pull/20099
- Turns out glTF also uses its viewport coordinate system for lights, so
let's account for that
## Solution
- Use the same branch for lights as for cameras when importing glTFs
## Testing
- Ran a dedicated Blender test scene to verify that this is the correct
behavior.
# Objective
Derived `TypePath` impls have a `const _` block in order to hold a `use
alloc::string::ToString` import. But that import is useless.
Avoiding it helps with https://github.com/bevyengine/bevy/issues/19873.
## Solution
Remove the `use` and `const _`.
## Testing
I used cargo expand to confirm the `const _ ` is no longer produced.
`-Zmacro-stats` output tells me this reduces the size of the `Reflect`
code produced for `bevy_ui` from 1_880_486 bytes to 1_859_634 bytes, a
1.1% drop.
## Problem
This pseudocode was triggering constantly, even with the Changed<> query
filter.
```rust
pub fn check_mouse_movement_system(
relative_cursor_positions: Query< &RelativeCursorPosition, (Changed<RelativeCursorPosition>, With<Button>)>,
) {}
```
## Solution
- Added a check to prevent updating the value if it hasn't changed.
---------
Co-authored-by: Giacomo Stevanato <giaco.stevanato@gmail.com>
# Objective
`SystemSetNode` doesn't really add much value beyond a couple helper
functions for getting the name as a string and checking if its a
`SystemTypeSet`. We can replace it entirely and use `InternedSystemSet`
directly by inlining these helper functions' usages without sacrificing
readability.
## Solution
Remove it and replace it with direct `InternedSystemSet` usage.
## Testing
Reusing current tests.
Notifications now include the source entity. This is useful for
callbacks that are responsible for more than one widget.
Part of #19236
This is an incremental change only: I have not altered the fundamental
nature of callbacks, as this is still in discussion. The only change
here is to include the source entity id with the notification.
The existing examples don't leverage this new field, but that will
change when I work on the color sliders PR.
I have been careful not to use the word "events" in describing the
notification message structs because they are not capital-E `Events` at
this time. That may change depending on the outcome of discussions.
@alice-i-cecile
# Objective
- Add 1-bounce RT GI
## Solution
- Implement a very very basic version of ReSTIR GI
https://d1qx31qr3h6wln.cloudfront.net/publications/ReSTIR%20GI.pdf
- Pretty much a copy of the ReSTIR DI code, but adjusted for GI.
- Didn't implement add more spatial samples, or do anything needed for
better quality.
- Didn't try to improve perf at all yet (it's actually faster than DI
though, unfortunately 😅)
- Didn't spend any time cleaning up the shared abstractions between
DI/GI
---
## Showcase
<img width="2564" height="1500" alt="image"
src="https://github.com/user-attachments/assets/1ff7be1c-7d7d-4e53-8aa6-bcec1553db3f"
/>
# Objective
I was building out a diagnostics panel in egui when I noticed that I
could specify the max history length for the
`FrameTimeDiagnosticsPlugin`, but not for the
`EntityCountDiagnosticsPlugin`. The objective was to harmonize the two,
making the diagnostic history length configurable for both.
## Solution
I added a `EntityCountDiagnosticsPlugin::new`, and a `Default` impl that
matches `FrameTimeDiagnosticsPlugin`.
This is a breaking change, given the plugin had no fields previously.
## Testing
I did not test this.
# Objective
Clean up documentation around `UninitializedId`, which has slightly
confusing semantics.
## Solution
Added documentation comments on `UninitializedId`.
---------
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
# Objective
- Another step towards unifying our orthonormal basis construction
#20050
- Preserve behavior but fix a bug. Unification will be a followup after
these two PRs and will need more thorough testing.
## Solution
- Make shadow cubemap sampling orthonormalize have the same function
signature as the other orthonormal basis functions in bevy
## Testing
- 3d_scene + lighting examples
# Objective
Picking was changed in the UI transform PR to walk up the tree
recursively to check if an interaction was on a clipped node.
`OverrideClip` only affects a node's local clipping rect, so valid
interactions can be ignored if a node has clipped ancestors.
## Solution
Add a `Without<OverrideClip>` query filter to the picking systems'
`child_of_query`s.
## Testing
This modified `button` example can be used to test the change:
```rust
//! This example illustrates how to create a button that changes color and text based on its
//! interaction state.
use bevy::{color::palettes::basic::*, input_focus::InputFocus, prelude::*, winit::WinitSettings};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
// Only run the app when there is user input. This will significantly reduce CPU/GPU use.
.insert_resource(WinitSettings::desktop_app())
// `InputFocus` must be set for accessibility to recognize the button.
.init_resource::<InputFocus>()
.add_systems(Startup, setup)
.add_systems(Update, button_system)
.run();
}
const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15);
const HOVERED_BUTTON: Color = Color::srgb(0.25, 0.25, 0.25);
const PRESSED_BUTTON: Color = Color::srgb(0.35, 0.75, 0.35);
fn button_system(
mut input_focus: ResMut<InputFocus>,
mut interaction_query: Query<
(
Entity,
&Interaction,
&mut BackgroundColor,
&mut BorderColor,
&mut Button,
&Children,
),
Changed<Interaction>,
>,
mut text_query: Query<&mut Text>,
) {
for (entity, interaction, mut color, mut border_color, mut button, children) in
&mut interaction_query
{
let mut text = text_query.get_mut(children[0]).unwrap();
match *interaction {
Interaction::Pressed => {
input_focus.set(entity);
**text = "Press".to_string();
*color = PRESSED_BUTTON.into();
*border_color = BorderColor::all(RED.into());
// The accessibility system's only update the button's state when the `Button` component is marked as changed.
button.set_changed();
}
Interaction::Hovered => {
input_focus.set(entity);
**text = "Hover".to_string();
*color = HOVERED_BUTTON.into();
*border_color = BorderColor::all(Color::WHITE);
button.set_changed();
}
Interaction::None => {
input_focus.clear();
**text = "Button".to_string();
*color = NORMAL_BUTTON.into();
*border_color = BorderColor::all(Color::BLACK);
}
}
}
}
fn setup(mut commands: Commands, assets: Res<AssetServer>) {
// ui camera
commands.spawn(Camera2d);
commands.spawn(button(&assets));
}
fn button(asset_server: &AssetServer) -> impl Bundle + use<> {
(
Node {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
children![(
Node {
width: Val::Px(0.),
height: Val::Px(0.),
overflow: Overflow::clip(),
..default()
},
children![(
//OverrideClip,
Button,
Node {
position_type: PositionType::Absolute,
width: Val::Px(150.0),
height: Val::Px(65.0),
border: UiRect::all(Val::Px(5.0)),
// horizontally center child text
justify_content: JustifyContent::Center,
// vertically center child text
align_items: AlignItems::Center,
..default()
},
BorderColor::all(Color::WHITE),
BorderRadius::MAX,
BackgroundColor(Color::BLACK),
children![(
Text::new("Button"),
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 33.0,
..default()
},
TextColor(Color::srgb(0.9, 0.9, 0.9)),
TextShadow::default(),
)]
)],
)],
)
}
```
On main the button ignores interactions, with this PR it should respond
correctly.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Noticed that we're converting perceptual_roughness to roughness for SSAO
specular occlusion up here, _but_ that happens _before_ we sample the
metallic_roughness texture map. So we're using the wrong roughness. I
assume this is a bug and was not intentional.
Suggest reviewing while hiding the whitespace diff.
## Objective
Fixes#20058
## Solution
Fix the `dynamic_offsets` array being too small if a mesh has morphs and
skins and motion blur, and the renderer isn't using storage buffers
(i.e. WebGL2). The bug was introduced in #13572.
## Testing
- Minimal repro: https://github.com/M4tsuri/bevy_reproduce.
- Also examples `animated_mesh`, `morph_targets`,
`test_invalid_skinned_meshes`.
- As far as I can tell Bevy doesn't have any examples or tests that can
repro the problem combination.
Tested with WebGL and native, Win10/Chrome/Nvidia.
# Objective
- `component.rs` is becoming quite big, let's split it.
## Solution
- Split `component.rs` into the following:
- `component/mod.rs`: the new root of the `component` module: contains
the `Component` trait and a few other very generic items
- `component/clone.rs`: contains component cloning related items
- `component/tick.rs`: contains `Tick` and other tick related items
- future possibilities: move these into `change_detection`; however I
wanted to keep this PR without breaking changes
- `component/register.rs`: contains component registration (included
queued) related items
- `component/required.rs`: contains items and functions for properly
computing required components
- `component/info.rs`: contains items for storing component info and
metadata in the `World`
# Objective
The current Entity Debug impl prints the bit representation. This is an
"overshare". Debug is in many ways the primary interface into Entity, as
people derive Debug on their entity-containing types when they want to
inspect them. The bits take up too much space in the console and
obfuscate the useful information (entity index and generation).
## Solution
Use the Display implementation in Debug as well. Direct people
interested in bits to `Entity::to_bits` in the docs.
# Objective
- Rebase of https://github.com/bevyengine/bevy/pull/12561 , note that
this is blocked on "up-streaming
[iyes_perf_ui](https://crates.io/crates/iyes_perf_ui)" , but that work
seems to also be stalled
> Frame time is often more important to know than FPS but because of the
temporal nature of it, just seeing a number is not enough. Seeing a
graph that shows the history makes it easier to reason about
performance.
## Solution
> This PR adds a bar graph of the frame time history.
>
> Each bar is scaled based on the frame time where a bigger frame time
will give a taller and wider bar.
>
> The color also scales with that frame time where red is at or bellow
the minimum target fps and green is at or above the target maximum frame
rate. Anything between those 2 values will be interpolated between green
and red based on the frame time.
>
> The algorithm is highly inspired by this article:
https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times
## Testing
- Ran `cargo run --example fps_overlay --features="bevy_dev_tools"`
---------
Co-authored-by: IceSentry <c.giguere42@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>