11380 Commits

Author SHA1 Message Date
Carter Anderson 83a067d4eb Auto nest bsn! tuples (#23726)
# Objective

Support an arbitrary number of `Scene` entries (ex: component patches)
in the `bsn!` macro.

## Solution

Auto nest the resulting `bsn!` tuple.
2026-04-08 22:33:14 +00:00
dependabot[bot] dd90d23b47 Update toml requirement from 0.8.19 to 1.1.0 (#23634)
Updates the requirements on [toml](https://github.com/toml-rs/toml) to
permit the latest version.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/toml-rs/toml/commit/d66e46e2c3f91e1d6d6479c5decea0993c2c76ba"><code>d66e46e</code></a>
chore: Release</li>
<li><a
href="https://github.com/toml-rs/toml/commit/8a05aef303b194e0b6fc07ecddd2968243e9f9ef"><code>8a05aef</code></a>
docs: Update changelog</li>
<li><a
href="https://github.com/toml-rs/toml/commit/dae17528484ebfd8c223bff6e34e2fb2df84d0bf"><code>dae1752</code></a>
chore: Bump to Edition 2024 (<a
href="https://redirect.github.com/toml-rs/toml/issues/1124">#1124</a>)</li>
<li><a
href="https://github.com/toml-rs/toml/commit/88aaa9ceec8d3dd71333f2a54b0c10ed175c2ecc"><code>88aaa9c</code></a>
chore: Bump to Edition 2024</li>
<li><a
href="https://github.com/toml-rs/toml/commit/35ae47fb75ed61950370353c2782474b6ea78ba3"><code>35ae47f</code></a>
refactor(bench): Rename away from 'gen'</li>
<li><a
href="https://github.com/toml-rs/toml/commit/7f439365135f9c833c145b1c64fd6718844af7ac"><code>7f43936</code></a>
style: Remove redundant ref</li>
<li><a
href="https://github.com/toml-rs/toml/commit/24a472a8b1494970a66f085509a2844d5236a5bb"><code>24a472a</code></a>
refactor: Use core::error::Error with MSRV 1.85</li>
<li><a
href="https://github.com/toml-rs/toml/commit/b4c084065e88190b83b9efc60e75da924e7f84f1"><code>b4c0840</code></a>
chore: Bump MSRV to 1.85</li>
<li><a
href="https://github.com/toml-rs/toml/commit/90790723370aa4981bafe054633c928eb78bcf94"><code>9079072</code></a>
chore: Release</li>
<li><a
href="https://github.com/toml-rs/toml/commit/06f2ba38f2377ab01b46c8acc1c4536254c24a50"><code>06f2ba3</code></a>
docs: Update changelog</li>
<li>Additional commits viewable in <a
href="https://github.com/toml-rs/toml/compare/toml-v0.8.19...toml-v1.1.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-08 21:16:35 +00:00
Chris Russell b76418987e Don't remove entry from ResourceEntities when removing a resource by ID (#23716)
# Objective

Make the behavior of `World::remove_resource` and
`World::remove_resource_by_id` consistent. Currently, `remove_resource`
will leave the entity in the `ResourceEntities` map so that
re-initializing the resource keeps the same `Entity`, but
`remove_resource_by_id` will remove it from the map.

## Solution

Change `remove` to `get` in `remove_resource_by_id`.  

## Testing

Updated the existing unit test to ensure that re-initializing the
resource keeps the same `Entity`.
2026-04-08 19:30:35 +00:00
DavidCrossman 6855864951 Use absolute paths in ECS macros (#23718)
# Objective

- Improve macro hygiene.

## Solution

- Use absolute paths in ECS macros.

## Testing

- `cargo run -p ci -- compile`

## Note

- The `SettingsGroup` derive is unhygienic (bare `impl SettingsGroup for
...`). I tried prepending the path from `BevyManifest` and adding
`extern crate self as bevy_settings` to `bevy_settings`'s lib. This
worked for the unit tests in `bevy_settings`, but the examples using
`bevy_settings` failed to resolve the path. I haven't investigated
further to keep this PR simple, but I can open an issue for fixing the
hygiene of `#[derive(SettingsGroup)]`.
- Many other macros in Bevy don't use absolute paths as much as they
could, such as in `bevy_reflect`. I'll open another PR if this one is
accepted.
2026-04-08 16:46:00 +00:00
Chris Russell 6a5ef7388f Change ResourceEntities from SparseSet to SparseArray to speed up resource lookups (#23616)
# Objective

Reduce memory usage for resources, and maybe improve performance of
resource lookups.

Related to #23039, but not a solution.

## Solution

Change `ResourceEntities` from a `SparseSet` to a `SparseArray`.  

`SparseArray` is `pub (crate)`, so simply exposing it through `Deref`
would cause privacy errors. Instead, remove the `Deref` impl and add
wrapper methods for `iter`, `get`, and `remove`. Change the return types
from `&Entity` to `Entity` now that they aren't generic.

As background: A `SparseArray` is a simple `Vec<Option<V>>`, while a
`SparseSet` is a `SparseArray` that maps keys to dense indexes, combined
with dense arrays of keys and values. That requires a second array
operation to find the actual value, but can be much better for memory
usage when the values are large, since missing items only take up space
for a single index instead of an entire value.

But the values in `ResourceEntities` are `Entity`, which are already
small! A `SparseArray` will always be smaller on 64-bit systems, since
an `Entity` is the same size as a `usize`, and we don't need to store
the additional `dense` and `indices` arrays. So switching to
`SparseArray` will save a lookup *and* save memory.

One drawback is that we can no longer use the dense lists to iterate all
resources, so methods like `iter_resources` now need to scan all
component ids. I don't expect this to be a problem in practice, though.
`iter_resources` is rarely used, and O(components) isn't all that much
worse than O(resources). If it turns out to be an issue, it's also
possible to recover this data by querying the `IsResource` component.

## Testing

Inconclusive.  

I attempted to run benchmarks, both `bevymark` as in the linked issue
and `cargo bench -p benches --bench ecs`, but the results were too noisy
on my machine to reach any conclusions. And now that I look more
closely, we don't have many benches that even use resources!

---------

Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
2026-04-08 15:40:20 +00:00
loreball a5cbc9e6a3 Add new and severity based constructors to BevyError (#23684)
# Objective

Add constructors for making `BevyError`s with a specific severity.
Closes #23676.

## Solution

Add a `new` constructor plus 1 constructor for every possible
`Severity`.

## Testing

My eyes and a simple test case that constructs an error and tests if the
downcasting works.

---

## Showcase

A `BevyError` now has multiple constructor to create one with an
expected severity

```rust
use bevy::ecs::error::{BevyError, Severity};

let debug_error = BevyError::new(Severity::Debug, "This works with strings");

let warn_error = BevyError::warn("There's a constructor for each severity level");
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
2026-04-08 05:16:40 +00:00
Willem c981ecd53b Do not use FixedUpdate on breakout (#23709)
# Objective

The aim is to simplify the example somewhat. This also removes the
jitter that some experience when running the example.

Fixes #14239
Fixes #2349

## Solution

Use `Update` instead of `FixedUpdate` schedule.
 
## Testing
```sh
cargo run --example breakout
```
and 

```sh
cargo run --features bevy_debug_stepping --example breakout
```
2026-04-07 20:53:27 +00:00
Talin 8f251ffcb4 Feathers text input widget (#23645)
# Objective

Feathers text input widget
Part of #19236 

## Solution

Build a wrapper around `EditableText` that has feathers styling and
themes.

## Testing

WiP

---------

Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
2026-04-07 18:28:48 +00:00
Chamaloriz cf2d04ceca Fix panic on Android Quest Headset suspend event (#23683)
When Android sends a suspend event like leaving the Guardian boundary on
the Quest headset, the .unwrap() panics with NoEntities, crashing the
app.
2026-04-07 18:02:06 +00:00
IceSentry 11bd3c484d Merge car meshes in bevy_city and improve loading screen (#23574)
# Objective

- In the asset pack used for bevy_city the tires of all the cars are
separate meshes from the car body. In some of the cars even the doors
are also separate meshes.
- Since we don't animate these elements of the car we can trivially
combine all the meshes of each car scene into one single car mesh. This
helps a lot to keep performance manageable and eventually increase the
scale of bevy_city.

## Solution

- Wait for the assets to load and once they are loaded loop over all the
car scenes and combine their meshes. This is mainly possible because the
entire asset pack uses a single texture so we don't need a separate
material for each part of the mesh.
- To do this I also refactored a bit how each step happens when starting
the app. I also made sure that each step is reflected on the loading
screen.
- I also added a bit more docs

## Testing

- I ran bevy_city and confirmed that the tires are where they should be
relative to the mesh and that performance was better. I went from 90fps
to 112fps on my machine

---

## Showcase


https://github.com/user-attachments/assets/c2a10681-d148-4394-a3ca-fffe928fa09c
2026-04-07 17:44:30 +00:00
ickshonpe 104a8c35f8 PositionedGlyph fixes (#23695)
# Objective

* Parley's `GlyphRun::positioned_glyphs()` doesn't emit byte indices,
the byte_index and byte_length on `PositionedGlyph` aren't set to the
correct values.
* The `line_index` is set from an enumeration on the inner loop in
`editable_text_system` but it's the outer loop that iterates per line.

## Solution

* Remove the `byte_index` and `byte_length` fields from
`PositionedGlyph`.
* Enumerate the outer loop in `editable_text_system`.
2026-04-07 03:54:54 +00:00
Carter Anderson ac4561072c Impl Scene for Box<dyn Scene>. Same for SceneList (#23698)
# Objective

Sometimes being generic on the scene type isn't desirable (ex: `field:
Option<S: Scene>` requires defining `S`, even if you aren't setting the
scene).

Being able to erase scene types while still using them in scenes is
critical functionality.

## Solution

- Impl `Scene` for `Box<dyn Scene>`
- Impl `SceneList` for `Box<dyn SceneList>`

(I also snuck in a minor unrelated style fix. Forgive me 😄 )
2026-04-07 02:21:21 +00:00
Carter Anderson d2af4a565d Add FromTemplate derives to Components using Handle (#23696)
# Objective

A lot of Bevy's built in types don't derive FromTemplate, which is what
makes things like asset handles "templatable" in BSN:

```rust
bsn! {
  ImageNode { image: "path_to_image.png" }
}
```

## Solution

Derive `FromTemplate` for components that have handles.

In cases of fields with `Option<SOME_TEMPLATED_TYPE>`, I've added the
`#[template(OptionTemplate<XTemplate>)]` attribute, as these cases can't
use the blanket `Default + Clone` template implementation (which doesn't
do templating on the internal type). Later I'm likely to propose
something like `#[template(built_in)]` for standard "collection types"
to make this a little less boilerplatey.
2026-04-07 01:45:51 +00:00
taishi-sama 0b49ff72d4 Snap to view (#23674)
# Objective

- Adds ``SnapToView`` camera controller as solution to #23499 

## Solution

- Adds ``snap_to_view`` module behind ``free_camera`` feature flag 
## Testing

- Manually tested with ``cargo run --example free_camera_controller
--features="free_camera bevy_dev_tools"`` and ``cargo run --example
scene_viewer --features="free_camera bevy_dev_tools"``:
  - All six hotkeys works.
    - [LCtrl +] Numpad1  - Snap to front/back
    - [LCtrl +] Numpad3  - Snap to right/left
    - [LCtrl +] Numpad7  - Snap to top/bottom
- Tested on Linux (Wayland)
---

## Showcase


https://github.com/user-attachments/assets/cca71180-c273-473f-a168-9793438d861f

Add ``SnapToViewCamera`` component to any ``Camera3d`` entity. 
Change parameters inside ``SnapToViewCamera`` to change snap speed and
hotkeys.

```rust
app.add_plugins(SnapToViewPlugin);

commands.spawn((
    Camera3d::default(),
    SnapToViewCamera::default(),
));
```

---------

Co-authored-by: taishi-sama <alexandra.2002.mikh@gmail.com>
2026-04-07 00:07:13 +00:00
Willem 0d0fa6ad12 Use run_system instead of SystemState for ECS benches (#23687)
# Objective

We want to use `World::run_system` where possible - especially in older
code that did not have the benefit of using it when it was written.

This is a small step towards #23238. More work is needed on the unit
tests and possibly elsewhere if this approach has been verified.

## Solution
- Instead of `SystemState` I used a closure system and registered it.
- I called the registered system with `World::run_system` inside the
bench.

## Testing
I ran the benches effected and found that they did not regress on my
machine.

Quick check:
```sh
cargo bench -p benches --bench ecs -- 50000_entities_table
```
2026-04-06 23:52:54 +00:00
Chris Russell 249fc6f2ce More doc comments for SparseSet and SparseArray (#23685)
# Objective

The design and purpose of `SparseSet` is not immediately obvious, and
there is very little documentation explaining it.

## Solution

Write more doc comments for `SparseSet` and `SparseArray`.
2026-04-06 23:49:14 +00:00
Greeble 34555039ca Expose VisitAssetDependencies derive macro (#23681)
## Objective

Allow crates other than `bevy_asset` to derive `VisitAssetDependencies`
- previously it was private.

`VisitAssetDependencies` is useful when an asset contains non-asset
types with dependencies:

```rust
#[derive(Asset)]
struct AssetStruct(#[dependency] NonAssetStruct)

#[derive(VisitAssetDependencies)]
struct NonAssetStruct(#[dependency] Handle<Image>);
```

There are workarounds - `NonAssetStruct` could derive `Asset` instead,
or it could manually implement the `VisitAssetDependencies` trait. But
exposing `VisitAssetDependencies` seems the simplest solution.

## Solution

```diff
-pub use bevy_asset_macros::Asset;
+pub use bevy_asset_macros::{Asset, VisitAssetDependencies};
```

## Testing

The PR adds a compilation test.

```sh
cargo test -p bevy_asset
```
2026-04-06 23:42:22 +00:00
ickshonpe 1aa3396863 Text editing selection box rendering fix (#23679)
# Objective

`EditableText`'s selection rects are currently drawn on top of the text.
Instead they should be drawn below with an option to draw the selected
text in an alternative color.

## Solution

* New field on `selected_text_color: Option<Color>` on
`TextCursorStyle`.
* In `extract_text_sections`, if `selected_text_color` is `Some` and the
glyph is inside a selection rect, use `selected_text_color` to draw the
glyph.
* The z offset for text selection rects is changed so the selection
rects are drawn behind the glyphs.

## Testing

```
cargo run --example multiline_text_input
```

---------

Co-authored-by: Daniel Skates <zeophlite@gmail.com>
2026-04-06 23:40:31 +00:00
Máté Homolya 437d982e1d Atmosphere as entity migration guide (#23675)
# Objective

- Once PR #23651 is merged we need a migration guide for breaking
changes to Bevy's atmosphere.
- Post the release note changes as a new PR to review grammar/writing
separately

## Solution

- Write a migration guide detailing the breaking changes
- Adhere to a friendly non-technical tone 
- Explain the new way of scaling the atmosphere with `Transform`

## Out of scope
- Explain how to customize the apparent up axis (i.e. horizon's
orientation) -> this should follow intuitively but maybe I should add
this too? or maybe this belongs in a release note instead?
2026-04-06 23:38:14 +00:00
Chris Biscardi a026ab9b84 impl Clone for bevy_light Shader markers (#23671)
# Objective

Enable the use of `NotShadowReceiver` in `bsn!` by implementing `Clone`
(`Default` is already implemented).

```rust
error[E0277]: the trait bound `NotShadowReceiver: Clone` is not satisfied
   --> src/spawn_circle.rs:375:27
    |
375 |           world.spawn_scene(bsn! {
    |  ___________________________^
376 | |             #SpawnCircle
377 | |             SpawnCircle
378 | |             SpawnEventToTrigger({self.event})
...   |
394 | |             )]
395 | |         });
    | |_________^ the trait `Clone` is not implemented for `NotShadowReceiver`
    |
```

I also noticed `TransmittedShadowReceiver` would have the same issue, so
added `Clone` there as well.

---

An alternative would be to implement `FromTemplate` instead of `Default`
and `Clone`. I'm not sure which solution is preferred in general, but
these are Marker components so don't require any additional values.
2026-04-06 23:36:31 +00:00
Kevin Chen 9f90ff38b9 fix(brp): allow params and id to be undefined when deserializing (#23661)
# Objective

- Fixes #23643 
- Some methods for BRP have `params` as an `Option`. `id` itself also
seems to be an `Option`. If these are not provided in the request
object, it should not panic on the deserialization level. Individual
methods should handle it, and they do already when its necesssary.

## Solution

- during deserialization, `params` and `id` should just flatten the
nested `Option` instead of `unwrap` and assuming something is there

## Testing

- Wrote a regression test
- Tested executing a curl while the `server` example without params, and
it works
`curl -d'{"jsonrpc":"2.0","method":"world.list_components","id":1}' -X
POST -H "Accept: applcation/json" -H "Content-Type: application/json"
http://127.0.0.1:15702`
- Tested `schedule.graph` to ensure that it panics without params
provided on its own (it does) and that params are still read correctly
when provided (it does)
2026-04-06 23:17:11 +00:00
Scott G Shannon c6a115c9ba Fix for 22822 ComputedStates example (#23655)
# Objective

- Fixes #22822

## Solution

- Adds ALLOW_SAME_STATE_TRANSITIONS = false to ComputedStates in
computed_states example

## Testing

- Tested by running example on x86_64 Linux 4.19 + KDE. Behavior noted
in #22822 no longer occurs.

---------

Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
2026-04-06 23:15:55 +00:00
Máté Homolya 18596dc888 Atmosphere as entity with transforms (#23651)
# Objective

**Anecdotal feedback:** 
- no floating origin support for implementing large-scale worlds, forked
Bevy's atmosphere at
https://github.com/philpax/veldera/blob/main/crates/bevy_pbr_atmosphere_planet/NOTICE.md
- no custom up axis support, resorted to using a custom sky shader for
flight simulator with Z up coordinate system. Bevy's atmosphere appears
tilted at a 90 degree angle with no way of changing it.

## Solution

- Atmosphere component can be spawned stand-alone
- AtmosphereSettings remains on camera
- A closest-to-camera heuristic is used to pick the primary atmosphere
to render. Deliberately no multi-atmosphere support to keep the scope of
this PR small and self contained. See
https://github.com/mate-h/bevy/pull/19 at an attempt.
- `scene_units_to_m` removed in favor of using `Transform`
- Z up now possible by offsetting the viewer position to the equator
- Floating origin systems now possible
- Simplify the `AtmosphereBuffer` / `AtmosphereData` structs to just use
the plain extracted `GpuAtmosphere` struct. this reduces the complexity
of the struct in the mesh view bindings. Since atmosphere settings is
coupled with the rendering pipeline of the atmosphere this makes sense
architecturally.
- We no longer hard code the offset to the north pole from the planet
center in places.

**Why not multi atmosphere:**
The atmosphere uses multiple LUTs (lookup textures) to accelerate the
rendering performance. Some of them are not view dependent:
- Transmittance LUT
- Multiple scattering LUT
- Scattering / density LUTs

These can be coupled and rendered for each atmosphere individually.
However the remainder of the pipeline is view dependent:
- Aerial View LUT
- Sky View LUT
- Render Sky pass

In raymarched rendering mode, these LUTs can be skipped and only the
render sky pass runs sampling on all of the atmospheres with a raymarch
in screen space.

Further, the Sky View LUT uses a local reference frame to concentrate
texel density along the horizon's local up axis. This in turn means it's
coupled with both a _specific_ atmosphere's local coordinates as well as
the view's transform matrix. We cannot consider rendering both
atmospheres into a single LUT for this reason. So it has to be unique
for each pair of (view, atmosphere). Given two views and two atmospheres
we would need 4 of these Sky View LUTs and at some point, raymarched
rendering will become the less expensive option.

Lastly the Render Sky pass needs to happen once per view, we cannot
realistically composite them in sequence with simple dual-source
blending as we do with the scene, this would result in incorrect
scattering integration. This in turn means we need to bind ALL of the
luts calculated previously so a single render sky pass and render aerial
view lut - perhaps making use of array textures. Rely on unified
volumetric ingegration in the raymarching loop: for each light,for each
atmosphere, attenuate inscattering and transmittance along the path
integral. It is suffice to say this change is overall _too complex_ for
the time being and is likely the reason Unreal Engine also do not
support multiple atmospheres. For context: our research is based heavily
on Sebastian Hillarie's work, one of the Unreal graphics engineers.

That being said about multiple atmospheres - I am thinking of this PR as
a segway into unified volumetrics in Bevy. that is: Render the FogVolume
and Atmosphere in a single pass! Making use of the frustum aligned voxel
grid "froxel" approach to accelerate the rendering. This would
drastically increase the performance for scenes wanting to make use of
both the atmosphere and local fog volumes.

## Testing

- Ran the `examples/3d/atmosphere.rs` example.

---

## Showcase

(example screenshot unchanged compared to main.)

```rs
// Spawn earth atmosphere
commands.spawn(Atmosphere::earth(earth_medium));

commands.spawn((
    Camera3d::default(),
    // Can be adjusted to change the rendering quality
    AtmosphereSettings::default(),
));
```

---------

Co-authored-by: Emerson Coskey <emerson@coskey.dev>
2026-04-06 23:08:30 +00:00
andriyDev efc6464f9b Create types to store serializable data about the ECS schedule and provide tools for extracting this data. (#22520)
# Objective

- A step towards #10981.
- Allow us to extract data from an app about the schedules.

Here are also some **non-goals** for this PR. These are left as future
work:

- Extract every piece of data in the schedule. I've focused on a rough
set of information that should let us visualization the most important
parts.
- Provide utilities for interpreting the data. This PR is focused on
just extracting the data, interpreting it comes next.
- Any sort of dot graph tools.

## Solution

- Create ser/de compatible structs for representing schedule data.
- Create a function to get schedule data from an initialized `Schedule`.
- Create a plugin to automatically extract the schedule data for every
schedule in the `App`.
- Note this doesn't include other subapps, I'll also leave that to
another PR.
- Make `bevy_ecs` return edges that build passes added.
- Make `bevy_ecs` return the "build metadata" to the caller, and also
trigger as an event.

## Testing

- Added tests!
- Added an example - it outputs a ron file. I assume its all valid.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
2026-04-06 23:06:15 +00:00
Dylan Sechet 128450bdcc Add rectangular area lights (#23288)
# Objective

Adds initial support for rectangle area lights, closing #7662.

Implements Linearly Transformed Cosines ([Heitz et al,
2016](https://eheitzresearch.wordpress.com/415-2/)), with some tricks
from [these
slides](https://advances.realtimerendering.com/s2016/s2016_ltc_rnd.pdf)
and [the reference
implementation](https://github.com/selfshadow/ltc_code).

## Limitations
There's currently no support for:
- Anisotropic materials (there's [a follow-up
paper](https://aakashkt.github.io/ltc_anisotropic.html) that shouldn't
be too hard to implement on top of this, though)
- Shadows
- Textured lights
- Clustering

## Testing

-  Compared results to eevee/cycles, see showcase section.
- Ran the new example with `cargo run --example rect_light --features
free_camera`, and made sure everythign works at grazing angles and from
behind the light.
- Clearcoat and transmission could probably use more testing, I just
made sure nothing looked obviously broken.
 
---

## Showcase


<img width="1919" height="941" alt="Showcase"
src="https://github.com/user-attachments/assets/f506f05c-4869-4387-aeba-d16944497825"
/>
<img width="1919" height="941" alt="Screenshot From 2026-03-11 11-43-42"
src="https://github.com/user-attachments/assets/c4a44903-784f-4c3e-bd44-84c3fe7bff29"
/>


Varying ground plane roughness:


![grid](https://github.com/user-attachments/assets/4426e9df-cde0-450e-b3b7-5e8c2b9fbdcb)
2026-04-06 22:28:53 +00:00
Doonv 55a875b577 Add -Zno-embed-metadata Cargo flag to config_fast_builds.toml (#23694)
# Objective

Reduce the size of the `target` directory with Bevy builds.

## Solution

I found this
[blog](https://kobzol.github.io/rust/rustc/2025/06/02/reduce-cargo-target-dir-size-with-z-no-embed-metadata.html)
about `-Zno-embed-metadata`, Cargo flag that reduces the size of the
`target` directory by avoiding duplicate metadata or something.

## Testing

I compiled the `main` branch with and without this branch and the size
of the `target` directory went from 11.61G without the flag to 11.15G
with the flag. Not a huge difference, but still a 4% decrease.
2026-04-06 22:16:49 +00:00
Chris Biscardi fed46a1986 add template_value to prelude (#23669)
# Objective

`template_value` is not in the prelude, but seems useful enough to be.
It seems like the main way to include an existing component value in a
`bsn!` declaration since using `{transform}` and similar expressions
doesn't work.

```rust
// some Transform, maybe from an `In<Transform>` or other argument.
let transform = Transform::from_xyz(5., 0., 5.);

bsn! {
    #SomeThing
    template_value(transform)
}
```

## Solution

Add `template_value` to the prelude.
2026-04-06 19:33:52 +00:00
Chris Russell fcabe2a01b Add #[inline] and #repr[transparent] to ComponentIdSet to attempt to fix perf regression (#23471)
# Objective

Attempt to fix a performance regression from adding the `ComponentSet`
type in #23384. See #23464.

## Solution

Add `#[inline]` attributes to the new trait impls. I had added them to
the inherent methods, but forgot about the traits!

## Testing

I have not tested this at all, so I don't know whether it will actually
fix the issue! I don't even understand how #23384 could have affected
those benchmarks in the first place, since they don't seem to call any
methods on `Access` during the loop.

But these trait methods all simply delegate to methods on `FixedBitSet`,
so marking them `#[inline]` seems harmless at worst.
2026-04-06 04:41:15 +00:00
andriyDev ef69ba45b1 Return/log an error when attempting to load an empty asset path. (#23654)
# Objective

- Partially addresses
https://discord.com/channels/691052431525675048/749332104487108618/1489425547598762095
- Currently BSN will attempt to load a string whenever a handle is seen.
Unfortunately, BSN treats nothing to mean the empty string. The asset
system then tries to load the empty string and returns various unhelpful
error messages depending on your OS.
- This doesn't totally fix the issue - BSN should probably not be
loading these paths at all. But more generally the asset system should
just ignore empty paths entirely since it should never load a meaningful
path.

## Solution

- In every load variant, add a check for the empty path and log an error
or return an error in that case. We also return a default handle in that
case (where necessary).
- Intentionally don't handle `load_folder`. You could theoretically want
to load the root folder!

To address some alternative implementations:
- There isn't a common loading API that all roads lead to! Each one is a
little different, so we need to handle them in each case. We should
investigate collapsing some of these though.
- `AssetPath` still needs to be allowed to parse an empty path, since
for stuff like joining asset paths, that needs to support cases like
"only a subasset label", or "joining the empty path with some subdir
path".

## Testing

- Added a test to test all the load variants on `AssetServer`.
2026-04-05 16:40:15 +00:00
François Mockers f865af110a Automated test for Bevy through BRP (#23647)
# Objective

- Show how a Bevy app can be automated through BRP

## Solution

- Example app_under_test is a basic app with a randomly placed button.
when the button is clicked, the app quit
- Example automated_test click that button through BRP using
`WindowEvent` so that it goes through the whole of Bevy

## Testing

- Run both example and they both exit

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
2026-04-05 16:39:56 +00:00
Daniel Skates 1a48b3fc58 Don't unnecesarily enable reflection in bevy_render by removing the default feature of bevy_render (#23667)
# Objective

- https://github.com/bevyengine/bevy/pull/23446/changes#r3036236068
spotted that we enable reflect in render unnecessarily

## Solution

- Remove it

## Testing

- CI
- `cargo run --example server --features="bevy_remote"` + `curl
-d'{"jsonrpc":"2.0","method":"schedule.graph","id":1,"params":{"schedule_label":"Render"}}'
-X POST -H "Accept: applcation/json" -H "Content-Type: application/json"
http://127.0.0.1:15703` still works
2026-04-05 16:38:32 +00:00
Daniel Skates 8455e8f67d Add TextEditChange event (#23665)
# Objective

- There's no event when the pending `TextEdit`'s are consumed

## Solution

- Add the event

## Testing

- CI
2026-04-05 16:25:53 +00:00
ickshonpe 28f99e9618 Minimal editable text scrolling implementation (#23646)
# Objective

Add basic support for scrolling text horizontally and vertically to
EditableText.

## Solution

* New component `TextScroll`. Wraps a Vec2 offset that can be used to
set the scroll position.
* Clip overflowing editable text content automatically.
* New system `scroll_editable_text` sets `TextScroll` to keep the cursor
within view inside the `EditableText`'s content box.
* New `allow_newline` flag on `EditableText`. Allows insertion of
newlines with the enter key.
* `editable_text_system` previously ignored `TextLayout::linebreak`. Now
it's possible to change the line break settings.
* Scrolling only happens after a `TextEdit` is applied.

The implementation here is extremely simple but it has some flaws:
* Depending on the width of the node sometimes the cursor can get
clipped.
* `allow_newlines: false` doesn't block pasting in a section of text
with a newline
* No support for scroll margins yet.
* Drag-to-scroll seems to work, but I'm not super confident in it and it
may need some changes.

Went back and forth over whether `ScrollPosition` should be used instead
of a dedicated component, I think it's probably simpler with
`TextScroll` but not certain. Clipping doesn't use `Node`'s overflow API
and is handled automatically during extraction, always targeting the
content box.

## Testing

```
cargo run --example multiline_text_input
```
2026-04-05 10:24:03 +00:00
Chris Biscardi f4789f9f90 add template to prelude (#23670)
# Objective

similar to #23669 , `template` seems useful and is used in documentation
examples, as well as the [scene bsn
example](https://github.com/bevyengine/bevy/blob/6b0fb37e2c73cfb9181a06888162d1d2534cef99/examples/scene/bsn.rs#L2),
but is not in the prelude.

## Solution

Add `template` to the prelude
2026-04-05 05:49:57 +00:00
Carter Anderson 6b0fb37e2c Rename bevy_scene2 to bevy_scene (#23668)
# Objective

Part 3 of #23619 

## Solution

Renames `bevy_scene2` to `bevy_scene` and adds it to the prelude.
2026-04-05 02:19:43 +00:00
François Mockers fb2d3c29bd fix editable_text example after new text plugins added to DefaultPlugins (#23664)
# Objective

- Example `editable_text` crashes because of duplicated plugins

## Solution

- Remove the duplicated plugins

## Testing

- `cargo run -example editable_text`
2026-04-05 01:27:21 +00:00
andriyDev 6de2d06e8c Make the default for the HandleTemplate to use Handle::default(). (#23656)
# Objective

- Not setting any value to a handle in BSN results in BSN attempting to
load an empty path! This is not handled well (see #23654 for some fixes
there).
- Even with #23654, we would still get errors in the logs which is not
ideal.

## Solution

- Use the Handle variant for the template!
2026-04-04 21:15:53 +00:00
Sébastien Job a66cedd6c3 Fix bevy_remote not compiling on wasm (#23367)
# Objective

- Make it possible to enable `bevy_remote` feature on wasm target
without the `bevy_remote/http` feature (which doesn't compile on wasm),
thus allowing to use the transport-agnostic `RemotePlugin`.
- Retain behavior of `bevy_remote` having the `bevy_remote/http` feature
by default on non-wasm targets, so no breaking changes for users.

## Solution

- Disable default features (so no `http`) in
`bevy_internal/bevy_remote`.
- Enable default features (including `http`) on non-wasm builds in
`bevy_internal/bevy_remote`.
- Fix `bevy_remote` having some dependencies only used by the HTTP
transport (and not wasm-compatible) not under the `http` feature flag.

## Testing

Tested locally with a temporary example not included in this PR that
enabled `bevy_remote`, added `RemotePlugin` but not `RemoteHttpPlugin`,
and verified that it compiled for both native and wasm targets, as well
as building the other bevy_remote examples on native.

---

Try yourself by creating a new crate with the following dependencies on
`Cargo.toml`:

```toml
[dependencies]
bevy = { git = "https://github.com/splo/bevy.git", branch = "fix-bevy_remote-wasm", features = [
    "bevy_remote",
] }
```

Add the following to `main.rs`:

```rust
use bevy::{prelude::*, remote::RemotePlugin};

fn main() {
    App::new().add_plugins(DefaultPlugins).add_plugins(RemotePlugin::default()).run();
}
```

And build both for native and wasm targets to verify it compiles
successfully.

```shell
cargo build --target wasm32-unknown-unknown # Fails on main branch.
cargo build
```
2026-04-04 04:25:17 +00:00
Carter Anderson 535cf401cc Reframe old "scene" terminology as "world serialization" (#23630)
Part 2 of #23619 

In **Bevy 0.19** we are landing a subset of Bevy's Next Generation Scene
system (often known as BSN), which now lives in the `bevy_scene` /
`bevy::scene` crate. However the old `bevy_scene` system still needs to
stick around for a bit longer, as it provides some features that Bevy's
Next Generation Scene system doesn't (yet!):

1. It is not _yet_ possible to write a World _to_ BSN, so the old system
is still necessary for "round trip World serialization".
2. The GLTF scene loader has not yet been ported to BSN, so the old
system is still necessary to spawn GLTF scenes in Bevy.

For this reason, we have renamed the old `bevy_scene` crate to
`bevy_world_serialization`. If you were referencing `bevy_scene::*` or
`bevy::scene::*` types, rename those paths to
`bevy_world_serialization::*` and `bevy::world_serialization::*`
respectively.

Additionally, to avoid confusion / conflicts with the new scene system,
all "scene" terminology / types have been reframed as "world
serialization":

- `Scene` -> `WorldAsset` (as this was always just a World wrapper)
- `SceneRoot` -> `WorldAssetRoot`
- `DynamicScene` -> `DynamicWorld`
    - `DynamicScene::from_scene` -> `DynamicWorld::from_world_asset`
- `DynamicSceneBuilder` -> `DynamicWorldBuilder`
- `DynamicSceneRoot` -> `DynamicWorldRoot`
- `SceneInstanceReady` -> `WorldInstanceReady`
- `SceneLoader` -> `WorldAssetLoader`
- `ScenePlugin` -> `WorldSerializationPlugin`
- `SceneRootTemplate` -> `WorldAssetRootTemplate`
- `SceneSpawner` -> `WorldInstanceSpawner`
- `SceneFilter` -> `WorldFilter`
- `SceneLoaderError` -> `WorldAssetLoaderError`
- `SceneSpawnError` -> `WorldInstanceSpawnError`

Note that I went with `bevy_world_serialization` over
`bevy_ecs_serialization`, as that is what all of the internal features
described themselves as. I think it is both more specific and does a
better job of making itself decoupled from `bevy_ecs` proper.
2026-04-04 00:31:47 +00:00
dependabot[bot] 53ddd5e615 Bump actions/deploy-pages from 4.0.5 to 5.0.0 (#23633)
Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages)
from 4.0.5 to 5.0.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/deploy-pages/releases">actions/deploy-pages's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<h1>Changelog</h1>
<ul>
<li>Update Node.js version to 24.x <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> (<a
href="https://redirect.github.com/actions/deploy-pages/issues/404">#404</a>)</li>
<li>Add workflow file for publishing releases to immutable action
package <a
href="https://github.com/Jcambass"><code>@​Jcambass</code></a> (<a
href="https://redirect.github.com/actions/deploy-pages/issues/374">#374</a>)</li>
<li>Bump braces from 3.0.2 to 3.0.3 in the npm_and_yarn group across 1
directory <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> (<a
href="https://redirect.github.com/actions/deploy-pages/issues/360">#360</a>)</li>
<li>Make the rebuild dist workflow work nicer with Dependabot <a
href="https://github.com/yoannchaudet"><code>@​yoannchaudet</code></a>
(<a
href="https://redirect.github.com/actions/deploy-pages/issues/361">#361</a>)</li>
<li>Bump the non-breaking-changes group across 1 directory with 3
updates <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> (<a
href="https://redirect.github.com/actions/deploy-pages/issues/358">#358</a>)</li>
<li>Delete repeated sentence <a
href="https://github.com/garethsb"><code>@​garethsb</code></a> (<a
href="https://redirect.github.com/actions/deploy-pages/issues/359">#359</a>)</li>
<li>Update README.md <a
href="https://github.com/tsusdere"><code>@​tsusdere</code></a> (<a
href="https://redirect.github.com/actions/deploy-pages/issues/348">#348</a>)</li>
<li>Bump the non-breaking-changes group with 4 updates <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> (<a
href="https://redirect.github.com/actions/deploy-pages/issues/341">#341</a>)</li>
<li>Remove error message for file permissions <a
href="https://github.com/TooManyBees"><code>@​TooManyBees</code></a> (<a
href="https://redirect.github.com/actions/deploy-pages/issues/340">#340</a>)</li>
</ul>
<hr />
<p>See details of <a
href="https://github.com/actions/deploy-pages/compare/v4.0.5...v4.0.6">all
code changes</a> since previous release.</p>
<p>⚠️ For use with products other than GitHub.com, such as GitHub
Enterprise Server, please consult the <a
href="https://github.com/actions/deploy-pages/#compatibility">compatibility
table</a>.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/actions/deploy-pages/commit/cd2ce8fcbc39b97be8ca5fce6e763baed58fa128"><code>cd2ce8f</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/deploy-pages/issues/404">#404</a>
from salmanmkc/node24</li>
<li><a
href="https://github.com/actions/deploy-pages/commit/bbe2a950ee52d4f5cbe74e6d9d6a8803676e91d5"><code>bbe2a95</code></a>
Update Node.js version to 24.x</li>
<li><a
href="https://github.com/actions/deploy-pages/commit/854d7aa1b99e4509c4d1b53d69b7ba4eaf39215a"><code>854d7aa</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/deploy-pages/issues/374">#374</a>
from actions/Jcambass-patch-1</li>
<li><a
href="https://github.com/actions/deploy-pages/commit/306bb814f29679fd12f0e4b0014bc1f3a7e7f4bc"><code>306bb81</code></a>
Add workflow file for publishing releases to immutable action
package</li>
<li><a
href="https://github.com/actions/deploy-pages/commit/b74272834adc04f971da4b0b055c49fa8d7f90c9"><code>b742728</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/deploy-pages/issues/360">#360</a>
from actions/dependabot/npm_and_yarn/npm_and_yarn-513...</li>
<li><a
href="https://github.com/actions/deploy-pages/commit/72732942c639e67ea3f70165fd2e012dd6d95027"><code>7273294</code></a>
Bump braces in the npm_and_yarn group across 1 directory</li>
<li><a
href="https://github.com/actions/deploy-pages/commit/963791f01c40ef3eff219c255dbfb97a6f2c9f87"><code>963791f</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/deploy-pages/issues/361">#361</a>
from actions/dependabot-friendly</li>
<li><a
href="https://github.com/actions/deploy-pages/commit/51bb29d9d7bfe15d731c4957ce1887b5ae8c6727"><code>51bb29d</code></a>
Make the rebuild dist workflow safer for Dependabot</li>
<li><a
href="https://github.com/actions/deploy-pages/commit/89f3d10406f57ee86e6517a982b3fb0438bd6dc5"><code>89f3d10</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/deploy-pages/issues/358">#358</a>
from actions/dependabot/npm_and_yarn/non-breaking-cha...</li>
<li><a
href="https://github.com/actions/deploy-pages/commit/bce735589bbbfa569f1d2ac003277b590d743e4c"><code>bce7355</code></a>
Merge branch 'main' into
dependabot/npm_and_yarn/non-breaking-changes-99c12deb21</li>
<li>Additional commits viewable in <a
href="https://github.com/actions/deploy-pages/compare/d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e...cd2ce8fcbc39b97be8ca5fce6e763baed58fa128">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/deploy-pages&package-manager=github_actions&previous-version=4.0.5&new-version=5.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-03 07:28:03 +00:00
dependabot[bot] 2f891a8f9a Bump dtolnay/rust-toolchain from efa25f7f19611383d5b0ccf2d1c8914531636bf9 to 3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 (#23632)
Bumps
[dtolnay/rust-toolchain](https://github.com/dtolnay/rust-toolchain) from
efa25f7f19611383d5b0ccf2d1c8914531636bf9 to
3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/dtolnay/rust-toolchain/commit/3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9"><code>3c5f7ea</code></a>
Add 1.94.1 patch release</li>
<li>See full diff in <a
href="https://github.com/dtolnay/rust-toolchain/compare/efa25f7f19611383d5b0ccf2d1c8914531636bf9...3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-03 07:27:21 +00:00
Talin f13d3f9f2d Focus outlines (#23628)
# Objective

Part of #19236

## Solution

Implements basic focus outlines for feathers widgets using `Outline`.

## Testing

Manual testing

---------

Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
2026-04-03 06:17:44 +00:00
Doonv 842fa5c455 Replace unnecessary &Window in exit_on_all_closed with With<Window> (#23625)
Tiny optimization I came across while working on
https://github.com/bevyengine/bevy/pull/23624
2026-04-02 22:13:43 +00:00
GHowe 2e1acb9d88 added serde(default) to Transform (#23474)
# Objective

Need serde(Default) for Transform. I think it would be very useful in
cases where someone wants to add an object to their scene but doesn't
want to have to specify rotation or scale, e.g.:

```
5: (
  components: {
    "common::types::GameObjectKind": Rifle,
    "bevy_transform::components::transform::Transform": (
      translation: (1.5, 800.0, 0.0),
    ),
  },
),
```

## Solution

Added `#[cfg_attr(feature = "serialize", serde(default))]` before the
Transform struct.

## Testing

Successfully built bevy, but haven't tested extensively. Theoretically
shouldn't break existing functionality, since Deriving Transform was
invalid before this PR.
2026-04-02 19:23:25 +00:00
Luke Yoo 09ef09eaa5 UiWidgetsPlugins and InputDispatchPlugin are now in DefaultPlugins (#23346)
# Objective

-
[Overview](https://hackmd.io/@UrWywBGGTV6bLjORZhuuPw/BJNPlNCu-g#Overview)
- `UiWidgetsPlugins` is part of `DefaultPlugins`
- Prerequisite for [Remove `Interaction` from
`examples/*`](https://github.com/bevyengine/bevy/pull/23285)

## Solution

- `UIWidgetsPlugins` adds `InputDispatchPlugin`.
- `UIWidgetsPlugins` and `InputDispatchPlugin` are removed from
`FeathersPlugins`.
- `UIWidgetsPlugins` slips into `DefaultPlugins`.

## Testing

Following examples are relevant:
- examples/ui/widgets/feathers.rs
- examples/games/game_menu.rs

## Also

[Migration
Guide](https://github.com/micttyoid/bevy/blob/ui-widgets-to-default-2/_release-content/migration-guides/ui_widgets_plugins_and_input_dispatch_plugin_are_now_default.md)
- Also: e7bab9fb83

[TODO after merge](#issuecomment-4057934697)

---------

Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
2026-04-02 18:46:41 +00:00
ickshonpe 33dbf93bf3 Update ComputedNode for scrollbar thumbs in update_scrollbar_thumb (#23612)
# Objective

Update `ComputedNode` for scrollbar thumbs in `update_scrollbar_thumb`
to avoid delays and system ambiguities.

## Solution

* Removed `Node` from scrollbar thumb node entities. 
* Renamed `CoreScrollbarThumb` to `ScrollbarThumb`.
* `ScrollbarThumb` now requires all the UI components and gains `border`
and `border_radius` fields.
* `update_scrollbar_thumb` now updates `ScrollbarThumb` node's
`UiGlobalTransform` and `ComputedNode` in `PostLayout`.


## Testing

```
cargo run --example scrollbars
```

We need a better scrollbars example, added an issue #23622.

---------

Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2026-04-02 16:05:41 +00:00
ickshonpe 8c3facc625 taffy 0.10 update (#23605)
# Objective

https://github.com/DioxusLabs/taffy/releases/tag/v0.10.0

## Solution

* Updated taffy dependency to version `0.10`.
* Added new enum to `ui_node` `InlineDirection`, maps to `Direction` in
taffy (Bevy already has a `Direction` type in `bevy_ecs`).
* `Node` has a new `direction: InlineDirection` field.

---------

Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
2026-04-02 15:48:28 +00:00
Johan Klokkhammer Helsing dc2cd99082 Fix stale bind group race for Material2d (#23457)
# Objective

If a `GpuImage` was modified (`Assets<Image>::insert`) in the same frame
as a Material2d that used it, it could create a race, potentially
leading to the old `TextureView` reference being read when creating the
`Material2d` bind group, leading to the material bind group never
getting the updated `GpuImage` reference.

## Solution

Register `GpuImage` as a dependency of `PreparedMaterial2d`.

Not all `Material2d` implementations might use `Image` handles, so I
guess this potentially has some cost in terms of performance. Ideally
we'd add some sort of mechanism so this dependency is only added when
needed, but this fix focuses on the minimal change to make the current
implementation correct.

## Testing

I ran into this problem when trying to get bevy_magic_light_2d working
on bevy 0.18 together with bevy_ecs_tilemap.

Minimal(ish) test case:

For some reason it only triggers on 0.18.1 though, not on current main.

```
//! Test: Material2d reading a render target image that gets replaced on resize.
//!
//! Without the fix (PreparedMaterial2d depending on GpuImage), the red spinner
//! rendered through the Material2d quad stops updating after a window resize.

use bevy::{
    asset::uuid_handle,
    camera::{visibility::RenderLayers, RenderTarget},
    ecs::message::MessageReader,
    prelude::*,
    render::{
        render_asset::RenderAssets,
        render_resource::*,
        texture::GpuImage,
        Render, RenderApp, RenderSystems,
    },
    shader::ShaderRef,
    sprite_render::{Material2d, Material2dPlugin, PreparedMaterial2d},
    window::WindowResized,
};

const RENDER_TARGET: Handle<Image> = uuid_handle!("8bdbc7e2-ddc0-4264-9701-0a40476a4de6");
const SHOW_MATERIAL: Handle<ShowTextureMaterial> =
    uuid_handle!("9a2cb207-fe74-45ed-b2c8-7ad4fc504c57");


#[derive(AsBindGroup, Clone, TypePath, Asset)]
struct ShowTextureMaterial {
    #[texture(0)]
    #[sampler(1)]
    image: Handle<Image>,
}

impl Material2d for ShowTextureMaterial {
    fn fragment_shader() -> ShaderRef {
        "examples/show_texture.wgsl".into()
    }
}

#[derive(Component)]
struct Spinner;

fn main() {
    let mut app = App::new();
    app.add_plugins((
            DefaultPlugins,
            Material2dPlugin::<ShowTextureMaterial>::default(),
        ));

    // Adding a system with Commands to PrepareAssets perturbs the system
    // ordering enough to trigger the bug.
    app.sub_app_mut(RenderApp).add_systems(
        Render,
        (|_commands: Commands| {}).in_set(RenderSystems::PrepareAssets),
    );

    app
        .add_systems(Startup, setup)
        .add_systems(PreUpdate, handle_resize)
        .add_systems(Update, spin)
        .run();
}

fn setup(
    mut commands: Commands,
    mut images: ResMut<Assets<Image>>,
    mut materials: ResMut<Assets<ShowTextureMaterial>>,
    mut meshes: ResMut<Assets<Mesh>>,
) {
    images
        .insert(RENDER_TARGET.id(), create_render_target_image(1280, 720))
        .expect("insert");

    // Camera rendering to image target
    commands.spawn((
        Camera2d,
        Camera::default(),
        RenderTarget::Image(RENDER_TARGET.clone().into()),
        Name::new("target_camera"),
    ));

    // Camera rendering to screen
    commands.spawn((
        Camera2d,
        Camera { order: 1, ..default() },
        RenderLayers::layer(1),
        Name::new("screen_camera"),
    ));

    // Red spinner rendered to image target
    commands.spawn((
        Sprite {
            color: Color::srgb(1.0, 0.0, 0.0),
            custom_size: Some(Vec2::splat(100.0)),
            ..default()
        },
        Transform::default(),
        Spinner,
    ));

    // Blue spinner rendered to screen directly
    commands.spawn((
        Sprite {
            color: Color::srgb(0.0, 0.0, 1.0),
            custom_size: Some(Vec2::splat(100.0)),
            ..default()
        },
        Transform::from_xyz(300.0, 0.0, 0.0),
        RenderLayers::layer(1),
        Spinner,
    ));

    // Material2d quad showing the render target on screen
    materials
        .insert(SHOW_MATERIAL.id(), ShowTextureMaterial {
            image: RENDER_TARGET.clone(),
        })
        .expect("insert material");
    commands.spawn((
        Mesh2d(meshes.add(Mesh::from(Rectangle::new(400.0, 300.0)))),
        MeshMaterial2d(SHOW_MATERIAL.clone()),
        Transform::from_xyz(-200.0, 0.0, 1.0),
        RenderLayers::layer(1),
    ));
}

fn spin(mut q: Query<&mut Transform, With<Spinner>>, time: Res<Time<Real>>) {
    for mut t in &mut q {
        t.rotation = Quat::from_rotation_z(time.elapsed_secs());
    }
}

fn handle_resize(
    mut images: ResMut<Assets<Image>>,
    mut materials: ResMut<Assets<ShowTextureMaterial>>,
    mut resize_events: MessageReader<WindowResized>,
    mut last_size: Local<UVec2>,
) {
    for event in resize_events.read() {
        let w = event.width as u32;
        let h = event.height as u32;
        if w == 0 || h == 0 {
            continue;
        }
        let new_size = UVec2::new(w, h);
        if new_size == *last_size {
            continue;
        }
        *last_size = new_size;
        images
            .insert(RENDER_TARGET.id(), create_render_target_image(w, h))
            .expect("insert image");
        // Re-insert material so its bind group gets recreated with the new GpuImage
        materials
            .insert(SHOW_MATERIAL.id(), ShowTextureMaterial {
                image: RENDER_TARGET.clone(),
            })
            .expect("insert material");
    }
}

fn create_render_target_image(w: u32, h: u32) -> Image {
    let size = Extent3d { width: w, height: h, ..default() };
    let mut image = Image {
        texture_descriptor: TextureDescriptor {
            label: Some("render_target"),
            size,
            dimension: TextureDimension::D2,
            format: TextureFormat::bevy_default(),
            mip_level_count: 1,
            sample_count: 1,
            usage: TextureUsages::TEXTURE_BINDING
                | TextureUsages::COPY_DST
                | TextureUsages::RENDER_ATTACHMENT,
            view_formats: &[],
        },
        ..default()
    };
    image.resize(size);
    image
}
```

Without the fix the red rectangle will stop spinning after resizing the
window.
2026-04-02 15:08:31 +00:00
Carter Anderson 45e454a83b Rename bevy_scene to bevy_ecs_serialization (#23619)
# Objective

The first part of #23606.

## Solution

Rename `bevy_scene` to `bevy_ecs_serialization`. No other changes were
made.

## Testing

The `scene` example still works as expected.
2026-04-02 02:22:58 +00:00
Nico Zweifel a600afa00c bsn! macro code quality improvements: codegen, error handling, tests, docs (#23561)
# Objective

This originated from playing around with the `bsn!` macro to find out
how well the lsp integration works during actual usage when typing new
fields etc.

It started by adding `new_spanned` to report errors. I restructured as I
saw fit for now as some of the locations were quite heavily indented,
although I'm not opposed to doing it differently. I do think it is an
improvement overall.

## Changes
- reduces nesting and indents significantly.
- remove/cleanup some dead/unused code, see comments.
- adds `BsnCodegenCtx` to clean up the function signatures and to allow
accumulation of errors
- adds usage of `syn::Result` return types, allowing for optional short
circuit behavior.
- adds error handling to the parsing like duplicate detection etc.
- small tweaks here and there to fail gracefully/continue parsing.
- improve/fix parsing in `is_const`, add tests.
- add a `BsnTokenStream` trait

## Notes
- the PR is not about fmt support for the macro content.
- it's not perfect, but I think it's a good base and much better than
what we have now from a devex pov.
- uses proc tests for both `is_const` and `path_type`, making them real
unit tests that follow AAA without taking more space to do it, we can
also type them all out if that's not alright but I think it's okay here
for these tests.

## Testing

`cargo test`
`cargo bench`
`cargo run --example bsn`


<img width="1392" height="772" alt="image"
src="https://github.com/user-attachments/assets/6dfd9c8c-fd68-4e74-b30d-e7e79d81aa1e"
/>

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2026-04-02 00:09:14 +00:00