## 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>
# Objective
The extraction systems for materials, meshes, and skins previously
iterated over `RemovedComponents<ViewVisibility>` in addition to more
specific variants like `RemovedComponents<MeshMaterial3d<M>>`. This
caused each system to loop through and check many irrelevant despawned
entities—sometimes multiple times. With many material types, this
overhead added up and became noticeable in frames with many despawns.
<img width="1091" alt="Screenshot 2025-02-21 at 10 28 01 AM"
src="https://github.com/user-attachments/assets/63fec1c9-232c-45f6-9150-daf8751ecf85"
/>
## Solution
This PR removes superfluous `RemovedComponents` iteration for
`ViewVisibility` and `GlobalTransform`, ensuring that we only iterate
over the most specific `RemovedComponents` relevant to the system (e.g.,
material components, mesh components). This is guaranteed to match what
the system originally collected.
### Before (red) / After (yellow):
<img width="838" alt="Screenshot 2025-02-21 at 10 46 17 AM"
src="https://github.com/user-attachments/assets/0e06b06d-7e91-4da5-a919-b843eb442a72"
/>
Log plot to highlight the long tail that this PR is addressing.
# Objective
Make the schedule graph code more understandable, and replace some
panics with `Result`s.
I found the `check_edges` and `check_hierarchy` functions [a little
confusing](https://github.com/bevyengine/bevy/pull/19352#discussion_r2181486099),
as they combined two concerns: Initializing graph nodes for system sets,
and checking for self-edges on system sets. It was hard to understand
the self-edge checks because it wasn't clear what `NodeId` was being
checked against! So, let's separate those concerns, and move them to
more appropriate locations.
Fix a bug where `schedule.configure_sets((SameSet, SameSet).chain());`
would panic with an unhelpful message: `assertion failed: index_a <
index_b`.
## Solution
Remove the `check_edges` and `check_hierarchy` functions, separating the
initialization behavior and the checking behavior and moving them where
they are easier to understand.
For initializing graph nodes, do this on-demand using the `entry` API by
replacing later `self.system_set_ids[&set]` calls with a
`self.system_sets.get_or_add_set(set)` method. This should avoid the
need for an extra pass over the graph and an extra lookup.
Unfortunately, implementing that method directly on `ScheduleGraph`
leads to borrowing errors as it borrows the entire `struct`. So, split
out the collections managing system sets into a separate `struct`.
For checking self-edges, move this check later so that it can be
reported by returning a `Result` from `Schedule::initialize` instead of
having to panic in `configure_set_inner`. The issue was that `iter_sccs`
does not report self-edges as cycles, since the resulting components
only have one node, but that later code assumes all edges point forward.
So, check for self-edges directly, immediately before calling
`iter_sccs`.
This also ensures we catch *every* way that self-edges can be added. The
previous code missed an edge case where `chain()`ing a set to itself
would create a self-edge and would trigger a `debug_assert`.
# Objective
- `bevy_math` allows the `dead_code` lint on some private structs when
`alloc` is not enabled
- allowing lints is not allowed, we should use expect
## Solution
- Don't even compile the code if its expected to be dead instead of
allowing or expecting the lint
# Objective
- `bevy_render_macros` fails to build on its own:
```
error[E0432]: unresolved import `syn::Pat`
--> crates/bevy_render/macros/src/specializer.rs:13:69
|
13 | DeriveInput, Expr, Field, Ident, Index, Member, Meta, MetaList, Pat, Path, Token, Type,
| ^^^
| |
| no `Pat` in the root
| help: a similar name exists in the module: `Path`
|
note: found an item that was configured out
--> /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.104/src/lib.rs:485:15
|
485 | FieldPat, Pat, PatConst, PatIdent, PatLit, PatMacro, PatOr, PatParen, PatPath, PatRange,
| ^^^
note: the item is gated behind the `full` feature
--> /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/syn-2.0.104/src/lib.rs:482:7
|
482 | #[cfg(feature = "full")]
| ^^^^^^^^^^^^^^^^
```
## Solution
- Enable the `full` feature of `syn`
# Objective
- bevy_log is not "others". it's part of Bevy
## Solution
- Move it
- Also use the same format as other Bevy dependency (path first) as I
use it in some regexes to run tests on the repo
# Objective
- Related to #19024.
## Solution
- This is a mix of several ways to get rid of weak handles. The primary
strategy is putting strong asset handles in resources that the rendering
code clones into its pipelines (or whatever).
- This does not handle every remaining case, but we are slowly clearing
them out.
## Testing
- `anti_aliasing` example still works.
- `fog_volumes` example still works.
# Objective
The false and true arguments for the select statement in `lerp_hue_long`
are misordered, resulting in it taking the wrong hue path:

## Solution
Swap the arguments around.
Also fixed another case I found during testing. The hue was interpolated
even when it is undefined for one of the endpoints (for example in a
gradient from black to yellow). In those cases it shouldn't interpolate,
instead it should return the hue of the other end point.
## Testing
I added a `linear_gradient` module to the testbed `ui` example, run
with:
```
cargo run --example testbed_ui
```
In the linear gradients screen (press space to switch) it shows a column
of red to yellow linear gradients. The last gradient in the column uses
the OKLCH long path, which should look like this:

matching the same gradient in CSS:
https://jsfiddle.net/fevshkdy/14/
if the correct hue path is chosen.
# Objective
`ThreadLocal::<T>::default()` doesn't require `T: Default`, so
`Parallel<T>` shouldn't require it either.
## Solution
- Replaced the `Default` derive with a manually specified impl.
- Added `Parallel::borrow_local_mut_or` as a non-`T: Default`-requiring
alternative to `borrow_local_mut`.
- Added `Parallel::scope_or` as a non-`T: Default`-requiring alternative
to `scope`.
# Objective
Add interpolation in HSL and HSV colour spaces for UI gradients.
## Solution
Added new variants to `InterpolationColorSpace`: `Hsl`, `HslLong`,
`Hsv`, and `HsvLong`, along with mix functions to the `gradients` shader
for each of them.
#### Limitations
* Didn't include increasing and decreasing path support, it's not
essential and can be done in a follow up if someone feels like it.
* The colour conversions should really be performed before the colours
are sent to the shader but it would need more changes and performance is
good enough for now.
## Testing
```cargo run --example gradients```
## Objective
Fixes#19884.
## Solution
- Add an internal entity command `insert_with`, which takes a function
returning a component and checks if the component would actually be
inserted before invoking the function.
- Add the same check to `insert_from_world`, since it's a similar
situation.
- Update the `or_insert_with`, `or_try_insert_with`, and `or_default`
methods on `EntityEntryCommands` to use the new command.
Since the function/closure returning the component now needs to be sent
into the command (rather than being invoked before the command is
created), the function now has `Send + 'static` bounds. Pretty typical
for command stuff, but I don't know how/if it'll affect existing users.
---------
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
# Objective
To implement fmt::Display for the direction types. The reason that this
would be a good addition is that I often find myself using println! to
debug things with directions and adding the extra ":?" was getting a
little annoying. It would also be better for any potential CLI apps that
might need to output a direction.
## Solution
Copied glam's implementation of Display for each length of direction.
I.E Vec3's display for Dir3.
## Testing
- Did you test these changes? If so, how?
Yes, I wrote a little script that printed out the different directions
and compared it to their vector counterparts.
Here it is if anyone's interested
```
use bevy_math::*;
fn main() {
let dir2 = Dir2::from_xy(0.0, 1.0).unwrap();
let dir3 = Dir3::from_xyz(0.0, 1.0, 0.0).unwrap();
let dir3a = Dir3A::from_xyz(0.0, 1.0, 0.0).unwrap();
let dir4 = Dir4::from_xyzw(0.0, 1.0, 0.0, 0.0).unwrap();
let vec2 = Vec2::new(0.0, 1.0);
let vec3 = Vec3::new(0.0, 1.0, 0.0);
let vec4 = Vec4::new(0.0, 1.0, 0.0, 1.0);
println!("{dir2} {dir3} {dir3a} {dir4}");
println!("{vec2}, {vec3}, {vec4}")
}
```
- Are there any parts that need more testing?
Perhaps
# Objective
#19649 introduced new `*_if_new` and `*_by_bundle_id_*` variations to
`EntityClonerBuilder` filtering functionality, which resulted in
increase in method permutations - there are now 8 allow variants to
support various id types and 2 different insert modes.
## Solution
This PR introduces a new trait `FilterableIds` to unify all id types and
their `IntoIterator` implementations, which is somewhat similar to
`WorldEntityFetch`. It supports `TypeId`, `ComponentId` and `BundleId`,
allowing us to reduce the number of `allow` methods to 4: `allow`,
`allow_if_new`, `allow_by_ids`, `allow_by_ids_if_new`. The function
signature is a bit less readable now, but the docs mention types that
can be passed in.
## Testing
All existing tests pass, performance is unchanged.
---------
Co-authored-by: urben1680 <55257931+urben1680@users.noreply.github.com>
# Objective
There is a pattern that appears in multiple places, involving
`reflect_clone`, followed by `take`, followed by `map_err` that produces
a `FailedDowncast` in a particular form.
## Solution
Introduces `reflect_clone_and_take`, which factors out the repeated
code.
## Testing
`cargo run -p ci`
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
All the derived reflection methods currently have multiple trait bounds
on non-generic field types, which serve no purpose. The are emitted
because "emit bounds on all fields" is easier than "emit bounds on
fields that need them". But improving things isn't too hard.
Similarly, lots of useless `Any + Send + Sync` bounds exist on
non-generic types.
Helps a lot with #19873.
## Solution
Remove the unnecessary bounds by only emitting them if the relevant type
is generic.
## Testing
I used `cargo expand` to confirm the unnecessary bounds are no longer
produced.
`-Zmacro-stats` output tells me this reduces the size of the `Reflect`
code produced for `bevy_ui` by 21.2%.
Fixes#19594
The exact problem is described in that issue.
I improved the docs to guide anyone who has the the same issue I had.
I kept myself minimal, since the problem is relatively niche, hopefully
it will be enough if anyone else has that problem
I noticed that the `SpatialListener` asks to have a `Transform`
attached. It seemed weird that we didnt just use a require macro, so i
went ahead and did that
I also tweaked the system that plays audio to use a `&GlobalTransform`
instead of an `Option<&GlobalTransform>`
# Objective
`PickingPlugin` and `PointerInputPlugin` were kinda weird being both a
plugin and a resource.
## Solution
Extract the resource functionality of `PickingPlugin` and
`PointerInputPlugin` into new resources
## Testing
`mesh_picking` and `sprite_picking`
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
# Objective
- nice bevy::camera bevy::mesh bevy::light imports
- skip bevy_light in 2d
## Solution
- add optional crates to internal
- make light only included when building pbr
## Testing
- 3d_scene
# Objective
Fixes#16525Fixes#19710
## Solution
Not allocating a mesh if it is empty.
## Testing
I tested using the following minimum repro from #16525
```rust
use bevy::{asset::RenderAssetUsages, prelude::*, render::mesh::PrimitiveTopology};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
commands.spawn(Camera2d);
let mesh = Mesh::new(
PrimitiveTopology::TriangleList,
RenderAssetUsages::default(),
);
commands.spawn((
Mesh2d(meshes.add(mesh)),
MeshMaterial2d(materials.add(Color::hsl(180.0, 0.95, 0.7))),
));
}
```
I was able to test on webgl2 and windows native and the issue seems to
be resolved. I am not familiar with how mesh rendering works and feel
like just skipping meshes should cause issues but I did not notice any.
# Objective
- Fixes#19910.
## Solution
- First, allow extraction function to be FnMut instead of Fn. FnMut is a
superset of Fn anyway, and we only ever call this function once at a
time (we would never call this in parallel for different pairs of worlds
or something).
- Run the `RenderStartup` schedule in the extract function with a flag
to only do it once.
- Remove all the `MainRender` stuff.
One sad part here is that now the `RenderStartup` blocks extraction. So
for pipelined rendering, our simulation will be blocked on the first
frame while we set up all the rendering resources. I don't see this as a
big loss though since A) that is fundamentally what we want here -
extraction **has to** run after `RenderStartup`, and the only way to do
better is to somehow run `RenderStartup` in parallel with the first
simulation frame, and B) without `RenderStartup` the **entire** app was
blocked on initializing render resources during Plugin construction - so
we're not really losing anything here.
## Testing
- I ran the `custom_post_processing` example (which was ported to use
`RenderStartup` in #19886) and it still works.
# Objective
- the fast inverse sqrt trick hasnt been useful on modern hardware for
over a decade now
## Solution
- just use sqrt, modern hardware has a dedicated instruction which will
outperform approximations both in efficiency and accuracy
## Testing
- ran `atmosphere`
# Objective
- make lights usable without bevy_render
## Solution
- make a new crate for lights to live in
## Testing
- 3d_scene, lighting, volumetric_fog, ssr, transmission, pcss,
light_textures
Note: no breaking changes because of re-exports, except for light
textures, which were introduced this cycle so it doesn't matter anyways
# Objective
- Calculating gradients in variable-termination loop is bad, and we dont
need to here
## Solution
- Sample mip 0 always
## Testing
- volumetric_fog example
# Objective
- prepare bevy_light for split
- make struct named better
- put it where it belongs
## Solution
- do those things
## Testing
- 3d_scene, lighting
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- prepare bevy_light for split
## Solution
- split render world extract related cluster code from main world ecs
stuff
re-exports make this not breaking
# Objective
- for smallvec some crates specify default features false, other dont.
turns out we dont need them
## Solution
- remove
## Testing
- 3d_scene
# Objective
- prepare bevy_light for split
## Solution
- extract cascade module (this is not strictly necessary for bevy_light)
- clean up imports to be less globby and tangled
- move light specific stuff into light modules
- move light system and type init from pbr into new LightPlugin
## Testing
- 3d_scene, lighting
NOTE TO REVIEWERS: it may help to review commits independently.
# Objective
- Make bevy_light possible by making it possible to split out
clusterable into bevy_camera
## Solution
- move ClusteredDecal to cluster module
- Depends on #19957 (because of the imports shuffling around) (draft
until thats merged)
## Testing
- 3d_scene runs
Note: no breaking changes thanks to re-exports
# Objective
- Make bevy_light possible
## Solution
- Move non-light stuff out of light module (its a marker for whether a
material should cast shadows: thats a material property not a light
property)
## Testing
- 3d_scene runs