# 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
- 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
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```
[Explanation](https://bevyengine.org/learn/contribute/helping-out/explaining-examples/)
for the 3d shapes example.
This shares a lot of detail with the [2d
shapes](https://github.com/bevyengine/bevy/pull/19211) example, so it's
similar in structure. The explanation for why asset handles are not
components has been copied over for now with minor adjustment, I'll do
another editing pass on this to make it match the surrounding context
and focus before taking it out of drafts.
---------
Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
Co-authored-by: Carter Weinberg <weinbergcarter@gmail.com>
# Objective
- Fixes#13872 (also mentioned in #17167)
## Solution
- Added conditional padding fields to the shader uniform
## Alternatives
### 1- Use a UVec4
Replace the `u32` field in `MyExtension` by a `UVec4` and only use the
`x` coordinate.
(This was the original approach, but for consistency with the rest of
the codebase, separate padding fields seem to be preferred)
### 2- Don't fix it, unlist it
While the fix is quite simple, it does muddy the waters a tiny bit due
to `quantize_steps` now being a UVec4 instead of a simple u32. We could
simply remove this example from the examples that support WebGL2.
## Testing
- Ran the example locally on WebGL2 (and native Vulkan) successfully
# 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
- First step towards #279
## Solution
Makes the necessary internal data structure changes in order to allow
system removal to be added in a future PR: `Vec`s storing systems and
system sets in `ScheduleGraph` have been replaced with `SlotMap`s.
See the included migration guide for the required changes.
## Testing
Internal changes only and no new features *should* mean no new tests are
requried.
A few versions ago, wgpu made it possible to set shader entry point to
`None`, which will select the correct entry point in file where only a
single entrypoint is specified. This makes it possible to implement
`Default` for pipeline descriptors. This PR does so and attempts to
`..default()` everything possible.
# Objective
- This unblocks some work I am doing for #19887.
## Solution
- Rename `RenderGraphApp` to `RenderGraphExt`.
- Implement `RenderGraphExt` for `World`.
- Change `SubApp` and `App` to call the `World` impl.
# Objective
- Example `light_textures` exit if feature `pbr_light_textures` is not
enabled. this is checked in code instead of using `required-features`
- Same for `clustered_decals` and `par_clustered_decals`
- Those examples are also using `eprintln`
- Those examples are using `process:exit` to exit
## Solution
- Use `required-features`
- Use logs
- Use `AppExit`
# Objective
- Progress towards #19024.
## Solution
- Remove `Handle::Weak`!
If users were relying on `Handle::Weak` for some purpose, they can
almost certainly replace it with raw `AssetId` instead. If they cannot,
they can make their own enum that holds either a Handle or an AssetId.
In either case, we don't need weak handles!
Sadly we still need Uuid handles since we rely on them for "default"
assets and "invalid" assets, as well as anywhere where a component wants
to impl default with a non-defaulted asset handle. One step at a time
though!
# Objective
During the migration to required components a lot of things were changed
around and somehow the draw order for some UI elements ended up
depending on the system ordering in `RenderSystems::Queue`, which can
sometimes result in the elements being drawn in the wrong order.
Fixes#19674
## Solution
* Added some more `stack_z_offsets` constants and used them to enforce
an explicit ordering.
* Removed the `stack_index: u32` field from `ExtractedUiNodes` and
replaced it with a `z_order: f32` field.
These changes should fix all the ordering problems.
## Testing
I added a nine-patched bordered node with a navy background color to the
slice section of the `testbed_ui` example.
The border should always be drawn above the background color.
# Objective
Change `ScrollPosition` to newtype `Vec2`. It's easier to work with a
`Vec2` wrapper than individual fields.
I'm not sure why this wasn't newtyped to start with. Maybe the intent
was to support responsive coordinates eventually but that probably isn't
very useful or straightforward to implement. And even if we do want to
support responsive coords in the future, it can newtype `Val2`.
## Solution
Change `ScrollPosition` to newtype `Vec2`.
Also added some extra details to the doc comments.
## Testing
Try the `scroll` example.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Because we want to be able to support more notification options in the
future (in addition to just using registered one-shot systems), the
`Option<SystemId>` notifications have been changed to a new enum,
`Callback`.
@alice-i-cecile
Currently, our specialization API works through a series of wrapper
structs and traits, which make things confusing to follow and difficult
to generalize.
This pr takes a different approach, where "specializers" (types that
implement `Specialize`) are composable, but "flat" rather than composed
of a series of wrappers. The key is that specializers don't *produce*
pipeline descriptors, but instead *modify* existing ones:
```rs
pub trait Specialize<T: Specializable> {
type Key: SpecializeKey;
fn specialize(
&self,
key: Self::Key,
descriptor: &mut T::Descriptor
) -> Result<Canonical<Self::Key>, BevyError>;
}
```
This lets us use some derive magic to stick multiple specializers
together:
```rs
pub struct A;
pub struct B;
impl Specialize<RenderPipeline> for A { ... }
impl Specialize<RenderPipeline> for A { ... }
#[derive(Specialize)]
#[specialize(RenderPipeline)]
struct C {
// specialization is applied in struct field order
applied_first: A,
applied_second: B,
}
type C::Key = (A::Key, B::Key);
```
This approach is much easier to understand, IMO, and also lets us
separate concerns better. Specializers can be placed in fully separate
crates/modules, and key computation can be shared as well.
The only real breaking change here is that since specializers only
modify descriptors, we need a "base" descriptor to work off of. This can
either be manually supplied when constructing a `Specializer` (the new
collection replacing `Specialized[Render/Compute]Pipelines`), or
supplied by implementing `HasBaseDescriptor` on a specializer. See
`examples/shader/custom_phase_item.rs` for an example implementation.
## Testing
- Did some simple manual testing of the derive macro, it seems robust.
---
## Showcase
```rs
#[derive(Specialize, HasBaseDescriptor)]
#[specialize(RenderPipeline)]
pub struct SpecializeMeshMaterial<M: Material> {
// set mesh bind group layout and shader defs
mesh: SpecializeMesh,
// set view bind group layout and shader defs
view: SpecializeView,
// since type SpecializeMaterial::Key = (),
// we can hide it from the wrapper's external API
#[key(default)]
// defer to the GetBaseDescriptor impl of SpecializeMaterial,
// since it carries the vertex and fragment handles
#[base_descriptor]
// set material bind group layout, etc
material: SpecializeMaterial<M>,
}
// implementation generated by the derive macro
impl <M: Material> Specialize<RenderPipeline> for SpecializeMeshMaterial<M> {
type Key = (MeshKey, ViewKey);
fn specialize(
&self,
key: Self::Key,
descriptor: &mut RenderPipelineDescriptor
) -> Result<Canonical<Self::Key>, BevyError> {
let mesh_key = self.mesh.specialize(key.0, descriptor)?;
let view_key = self.view.specialize(key.1, descriptor)?;
let _ = self.material.specialize((), descriptor)?;
Ok((mesh_key, view_key));
}
}
impl <M: Material> HasBaseDescriptor<RenderPipeline> for SpecializeMeshMaterial<M> {
fn base_descriptor(&self) -> RenderPipelineDescriptor {
self.material.base_descriptor()
}
}
```
---------
Co-authored-by: Tim Overbeek <158390905+Bleachfuel@users.noreply.github.com>
# Objective
- This example uses a FromWorld impl to initialize a resource on startup
- #19887
## Solution
- Use RenderStartup instead
## Testing
- The example still works as expected
# Objective
I was lurking and noticed that some links to the Bevy website were not
updated in newer code (`bevyengine.org` -> `bevy.org`).
## Solution
- Look for `bevyengine.org` occurrences in the current code, replace
them with `bevy.org`.
## Testing
- Did you test these changes? If so, how? I visited the Bevy website!
- Are there any parts that need more testing?
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
## Longer term
- Maybe add a lint to flag references to the old website but I don't
know how to do that. But not sure it's needed as the more time will pass
the less it will be relevant.
# Objective
Contributes to #18238
Updates the `log_layers_ecs`, example to use the `children!` macro.
Note that I did not use a macro, nor `Children::spawn` for the outer
layer. Since the `EventReader` is borrowed mutably, any `.map` I did on
`events.read()` was going to have the reference outlive the function
body. I believe this scope of change is correct for the PR.
## 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
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- A step towards #19024.
- `AnimationGraph` can serialize raw `AssetId`s. However for normal
handles, this is a runtime ID. This means it is unlikely that the
`AssetId` will correspond to the same asset after deserializing -
effectively breaking the graph.
## Solution
- Stop allowing `AssetId` to be serialized by `AnimationGraph`.
Serializing a handle with no path is now an error.
- Add `MigrationSerializedAnimationClip`. This is an untagged enum for
serde, meaning that it will take the first variant that deserializes. So
it will first try the "modern" version, then it will fallback to the
legacy version.
- Add some logging/error messages to explain what users should do.
Note: one limitation here is that this removes the ability to serialize
and deserialize UUIDs. In theory, someone could be using this to have a
"default" animation. If someone inserts an empty `AnimationClip` into
the `Handle::default()`, this **might** produce a T-pose. It might also
do nothing though. Unclear! I think this is worth the risk for
simplicity as it seems unlikely that people are sticking UUIDs in here
(or that you want a default animation in **any** AnimationGraph).
## Testing
- Ran `cargo r --example animation_graph -- --save` on main, then ran
`cargo r --example animation_graph` on this PR. The PR was able to load
the old data (after #19631).
# Objective
add support for light textures (also known as light cookies, light
functions, and light projectors)

## Solution
- add components:
```rs
/// Add to a [`PointLight`] to add a light texture effect.
/// A texture mask is applied to the light source to modulate its intensity,
/// simulating patterns like window shadows, gobo/cookie effects, or soft falloffs.
pub struct PointLightTexture {
/// The texture image. Only the R channel is read.
pub image: Handle<Image>,
/// The cubemap layout. The image should be a packed cubemap in one of the formats described by the [`CubemapLayout`] enum.
pub cubemap_layout: CubemapLayout,
}
/// Add to a [`SpotLight`] to add a light texture effect.
/// A texture mask is applied to the light source to modulate its intensity,
/// simulating patterns like window shadows, gobo/cookie effects, or soft falloffs.
pub struct SpotLightTexture {
/// The texture image. Only the R channel is read.
/// Note the border of the image should be entirely black to avoid leaking light.
pub image: Handle<Image>,
}
/// Add to a [`DirectionalLight`] to add a light texture effect.
/// A texture mask is applied to the light source to modulate its intensity,
/// simulating patterns like window shadows, gobo/cookie effects, or soft falloffs.
pub struct DirectionalLightTexture {
/// The texture image. Only the R channel is read.
pub image: Handle<Image>,
/// Whether to tile the image infinitely, or use only a single tile centered at the light's translation
pub tiled: bool,
}
```
- store images to the `RenderClusteredDecals` buffer
- read the image and modulate the lights
- add `light_textures` example to showcase the new features
## Testing
see light_textures example
# Objective
When dragging the slider thumb the thumb is only highlighted while the
pointer is hovering the widget. If the pointer moves off the widget
during a drag the thumb reverts to its normal unhovered colour.
## Solution
Query for `CoreSliderDragState` in the slider update systems and set the
lighter color if the thumb is dragged or hovered.
# Objective
This PR introduces Bevy Feathers, an opinionated widget toolkit and
theming system intended for use by the Bevy Editor, World Inspector, and
other tools.
The `bevy_feathers` crate is incomplete and hidden behind an
experimental feature flag. The API is going to change significantly
before release.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Closes#18075
In order to enable a number of patterns for dynamic materials in the
engine, it's necessary to decouple the renderer from the `Material`
trait.
This opens the possibility for:
- Materials that aren't coupled to `AsBindGroup`.
- 2d using the underlying 3d bindless infrastructure.
- Dynamic materials that can change their layout at runtime.
- Materials that aren't even backed by a Rust struct at all.
## Solution
In short, remove all trait bounds from render world material systems and
resources. This means moving a bunch of stuff onto `MaterialProperties`
and engaging in some hacks to make specialization work. Rather than
storing the bind group data in `MaterialBindGroupAllocator`, right now
we're storing it in a closure on `MaterialProperties`. TBD if this has
bad performance characteristics.
## Benchmarks
- `many_cubes`:
`cargo run --example many_cubes --release --features=bevy/trace_tracy --
--vary-material-data-per-instance`:

- @DGriffin91's Caldera
`cargo run --release --features=bevy/trace_tracy -- --random-materials`

- @DGriffin91's Caldera with 20 unique material types (i.e.
`MaterialPlugin<M>`) and random materials per mesh
`cargo run --release --features=bevy/trace_tracy -- --random-materials`

### TODO
- We almost certainly lost some parallelization from removing the type
params that could be gained back from smarter iteration.
- Test all the things that could have broken.
- ~Fix meshlets~
## Showcase
See [the
example](https://github.com/bevyengine/bevy/pull/19667/files#diff-9d768cfe1c3aa81eff365d250d3cbe5a63e8df63e81dd85f64c3c3cd993f6d94)
for a custom material implemented without the use of the `Material`
trait and thus `AsBindGroup`.

---------
Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
Co-authored-by: IceSentry <c.giguere42@gmail.com>