# Objective
#24206 boxes the large error in `GltfError` but it's not sufficient
after https://github.com/rust-lang/rust-clippy/issues/17070 is fixed.
## Solution
Box `AssetLoadError::RequestedHandleTypeMismatch` so that it is reduced
to 120 bytes which is below 128 bytes.
Also fix some other trivial clippy warnings.
## Testing
Use the latest nightly clippy which has
https://github.com/rust-lang/rust-clippy/issues/17070 fixed:
```
cargo +nightly clippy --workspace --all-features --all-targets -- -D warnings
```
# Objective
Add a `context()` extension method to `Result<T>` and `Option<T>` like
`anyhow`
## Solution
Add a `Vec<String>` field to `InnerBevyError` to store context messages
added via `context()`
Fixes#19714.
## Testing
Added the `context` unit test to test messages produced by `context() `
and a `context_downcasting` unit test to test that downcasting still
works when using `.context`
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- When `FontSource` resolution fails because an asset isn't yet loaded,
it doesn't attempt to reresolve the `FontSource` again the next frame.
- When a `Font` asset is removed, its font data is not unloaded from
Parley's font database
- The results from font lookups can be inconsistant when using `Handle`s
to identify a font.
Fixes#24356
## Solution
* In `load_font_assets_into_font_collection`, register each font twice,
once with an internal asset-specific alias for handle lookups and once
using its embedded family name.
* Use `set_changed` on `TextFont` to trigger chain detection, instead of
the `needs_rerender` flag on `TextBlock`. Schedule
`load_font_assets_into_collection` to run before
`detect_text_needs_rerender`, otherwise this would delay updates for a
frame.
* Store the changed family ids and asset paths, and set any `TextFont`s
that refer to them as changed.
* Don't update the measure funcs in `update_editable_text_content_size`
on font asset changes. Instead rely on the narrower `TextFont` change
detection, which is sufficient now because
`load_font_assets_into_font_collection` marks affected `TextFont`
components as changed when newly loaded font assets can affect font
resolution.
* Removed the mutable deref of `EditableText` at the start of
`update_editable_text_styles`. The editor is only mutable accessed if
updates to the styles need to be made.
* On unloading a font rebuild the whole font database, minus the
unloaded fonts, remap any generic families and relayout all text.
* Keep a copy of the registered generic families in `FontCx`, so they
can be remapped after unloading a font.
## Testing
```rust
use bevy::{
feathers::{controls::FeathersNumberInput, FeathersPlugins},
prelude::*,
};
fn main() {
App::new()
.add_plugins((DefaultPlugins, FeathersPlugins))
.add_systems(Startup, setup)
.add_systems(Update, spawn_number_input)
.run();
}
fn setup(mut commands: Commands) {
// ui camera
commands.spawn(Camera2d);
}
fn spawn_number_input(mut commands: Commands, mut is_spawned: Local<bool>) {
if *is_spawned {
return;
}
*is_spawned = true;
commands.spawn_scene(bsn! {
Node {
margin: UiRect::all(auto())
}
Outline
Children [
:FeathersNumberInput Node { width: px(200) }
]
});
}
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Simplify the use of `define_label!()`.
## Solution
- Move the `static` interner inside the `intern()` method, so that it is
not visible anywhere else and does not need to have a non-conflicting
name. The only operation the interner has is interning, so the
`intern()` trait method is all the access that is ever needed.
- Also added support for trailing commas.
This PR will conflict with #24444. I will rebase whichever one doesn’t
get merged first.
## Testing
- Ran tests with `cargo run -p ci -- --keep-going test`. There were some
failures that I believe are existing/spurious.
# Objective
Resolves#21902.
## Solution
This PR adopts a relatively transparent approach to reduce the GPU
vertex buffer size. On CPU-side mesh can still use uncompressed Float32
data, and users are not required to insert compressed vertex formats.
The vertex data is automatically processed into
lower-precision/octahedral encoded data when uploading to the GPU.
To enable vertex attribute compression, just set the
`attribute_compression` field of Mesh, or set
`mesh_attribute_compression` of GltfLoaderSettings. If enabled, normal
and tangent will be octahedral encoded Unorm16x2, uv0, uv1, joint weight
and color will be corresponding Unorm16 or Float16. I also provide
Unorm8x4 for vertex color if hdr isn't needed.
Update 2026-2-16
Removed previous approach that automatically compresses vertex buffer
according to flags when uploading to GPU. Instead, I added
`compressed_mesh` method to Mesh to construct compressed Mesh ahead of
time. GltfLoader can also opt-in mesh compressing when loading. I also
add an option to convert indices to u16, though I believe blender gltf
exporter already uses u16 indices when possible.
## Testing
Run `many_cubes`, `many_foxes`, `many_morph_targets` with
`--vertex-compression` to test 3d.
Run `bevymark` with `sprite_mesh` to test 2d, because `SpriteMesh` uses
compressed quad mesh now.
---------
Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com>
# Objective
Resolve `#[expect(clippy::result_large_err)]`.
## Solution
Box large variants in `GltfError` and `LoadDirectError` to reduce their
size. `GltfError` is reduced to 64 bytes and `LoadDirectError` is
reduced to 80 bytes.
## Testing
CI
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- Step towards making the extract infra is reusable for non-rendering
crates (see https://github.com/bevyengine/bevy/issues/24483 )
## Solution
- Replace `TemporaryRenderEntity` with a generic over `AppLabel`,
`TemporaryRenderEntity` becomes an alias over `RenderApp`
## Testing
- CI
- `cargo run --example animated_mesh`
## Objective
Change the generated code for `Typed::type_info()` to reduce release
build binary sizes by ~4.6MB. Also makes some very minor improvements to
runtime memory and compile times.
## Background
Say I have this reflected struct:
```rust
#[derive(Reflect)]
struct Example {
number: u32,
length: f32,
}
```
The generated code for `Example::type_info` is:
```rust
impl bevy_reflect::Typed for Example {
fn type_info() -> &'static bevy_reflect::TypeInfo {
static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
bevy_reflect::utility::NonGenericTypeInfoCell::new();
CELL.get_or_set(|| {
bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(
&[
bevy_reflect::NamedField::new::<u32>("number"),
bevy_reflect::NamedField::new::<f32>("length"),
],
))
})
}
}
```
So on the first call it creates a bunch of data structures that
represent the type (`StructInfo`, `NamedField` etc), and later calls
just return a reference to that data.
The assembly code for `type_info` can be surprisingly large -
`Example::type_info` is 2KB (x86, release). This might not sound like
much, but it adds up when there's over a thousand reflected types, and
some of them are much more complex - the assembly for
`bevy_ui::Node::type_info` is 26.5KB. In total there's several megabytes
depending on build settings (it's hard to be completely accurate due to
inlining and the way some things are hidden behind closures).
## Solution
This PR makes a few different changes to the generated code for
`type_info` and various supporting code. In a release build,
`Example:type_info` goes from 2KB to 126 bytes, and
`bevy_ui::Node::type_info` from 26.5KB to 10.1KB. The binary goes from
79.03MB to 74.38MB (-4.64MB, -5.9%).
### 1. Avoid allocations
Several structs contain an `Arc<CustomAttributes>`. But most types
doesn't have any custom attributes. Switching to
`Option<Arc<CustomAttributes>>` avoids allocating an `Arc` for the empty
case.
```diff
pub fn new(name: &'static str) -> Self {
Self {
name,
- custom_attributes: Arc::new(CustomAttributes::default()),
+ custom_attributes: None,
#[cfg(feature = "reflect_documentation")]
docs: None,
}
```
(**EDIT:** After feedback this approach was changed slightly - see
https://github.com/bevyengine/bevy/pull/24171#issuecomment-4506875895.)
Similarly, `Generics` is a `Box<[GenericInfo]>` that's usually empty.
```diff
-pub struct Generics(Box<[GenericInfo]>);
+pub struct Generics(Option<Box<[GenericInfo]>>);
```
(**EDIT:** A reviewer noted that an empty boxed slice does not allocate.
However, the change still decreases binary size. For details:
https://github.com/bevyengine/bevy/pull/24171#discussion_r3207800618.)
### 2. Reduce generic code
`StructInfo::new` and others look roughly like this:
```rust
pub fn new<T: Reflect + TypePath>(fields: &[NamedField]) -> Self {
...build internal boxes and HashMaps...
Self {
ty: Type::of::<T>(),
fields: fields.to_vec().into_boxed_slice(),
...
}
}
```
Being generic means that the whole function gets duplicated for each
unique `T`, and often gets inlined too. But `T` is only used once by
`Type::of::<T>()`.
This PR splits the function into a small generic stub that calls a
larger non-generic with `inline(never)`.
```rust
pub fn new<T: Reflect + TypePath>(fields: &[NamedField]) -> Self {
Self::from_erased(Type::of::<T>(), fields)
}
#[inline(never)]
pub fn from_erased<T: Reflect + TypePath>(fields: &[NamedField]) -> Self {
...
}
```
### 3. Reduce inlining
I've also made a few changes to inlining on `type_info`:
```diff
impl bevy_reflect::Typed for Example {
- #[inline]
+ #[inline(never)]
fn type_info() -> &'static bevy_reflect::TypeInfo {
static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
bevy_reflect::utility::NonGenericTypeInfoCell::new();
- CELL.get_or_set(|| {
+ CELL.get_or_set(#[inline(never)] || {
...
```
The first change might be contentious as it makes
`get_represented_type_info` +20% slower in micro-benchmarks - although
20% slower only means one cycle slower. I'd guess that's worth it to
save ~363KB - it might even be a performance increase in real code due
to memory latency. But it's debatable - I'd be fine to remove it.
The second change avoids inlining the closure since it can only be
called once - this gives a ~330KB saving with no performance cost.
Although I'm not quite sure *why* it's a saving. I suspect that non-LTO
release builds are ending up with two copies - one regular closure and
one inlined copy.
### Putting it all together
Here's the cumulative effect of each change on a release build of
`3d_scene` (Win10, x86).
| | |
|-|-|
| Original | 80,927KB |
| Change `Arc<CustomAttributes>` to option | 78,185KB (-2,742KB) |
| Change `Generics` to option | 77,959KB (-226KB) |
| Change `TypeInfo` variants to reduce generics | 76,866KB (-1,093KB) |
| Never inline `type_info` inner closure | 76,536KB (-330KB) |
| Never inline `type_info` itself | 76,173KB (-363KB) |
Compile times maybe went down a second or two (2m21s -> 2m19s), although
I'm not sure how much to trust that.
And the before/after on a few different builds:
| Build | Before | After | Difference
|-|-|-|-|
| x86 release | 79.0MB | 74.3MB | -4.7MB, -5.9% |
| x86 release optimized | 45.3MB | 43.9MB | -1.4MB, -3% |
| WASM release | 90.3MB | 88.7MB | -1.6MB, -1.7%) |
| WASM release optimized | 61.5MB | 60.6MB | -0.9MB, -1.5% |
The optimized build has `codegen-units = 1, lto = "fat", panic =
"abort"`.
## Can More Be Done?
Yes, I can imagine a bunch more ways to make `type_info` smaller. This
PR only does the easy stuff. But all the options I could think of are
more complex, and there will be diminishing returns.
The dream would be to make `TypeInfo` entirely `const` data. But that's
a major overhaul and might mean requiring it to leak memory - that's
fine for static reflection, but I don't know if it's also used where
leaking would be problematic.
A less drastic option would involve changing `type_info` to not use
`StructInfo`/etc directly - instead it would build up a bunch of smaller
POD types that don't need much copying and never need to free memory.
Then finally it would pass this simpler type to a non-inlined function
that actually creates the real `TypeInfo`. Would that be worth it? It
adds a lot of code, and there's a risk that the savings might be just a
few hundred KB.
## Testing
```sh
cargo test -p bevy_reflect
cargo test -p bevy_reflect_derive
cargo test -p bevy_asset
cargo run --example custom_attributes
# Benchmark type_info
cargo bench -p benches --bench reflect -- concrete_struct_type_info
```
---------
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
# Objective
Currently, a panic (whether from engine or user code, as there is little
distinction) takes down the entire app with it. Instead the user should
be able to decide how the error is handled. This is currently not
possible except by writing your own executor and setting it for all
relevant schedules.
See for comparison Godot's policy on exceptions:
> ### [Why does Godot not use
exceptions?](https://docs.godotengine.org/en/stable/about/faq.html#why-does-godot-not-use-exceptions)
>
> We believe games should not crash, no matter what. If an unexpected
situation happens, Godot > will print an error (which can be traced even
to script), but then it will try to recover as > gracefully as possible
and keep going.
Unity will also log an error and then continue if user code throws an
exception. I believe Unreal does too for exceptions coming from
Blueprints. Similarly, many web servers will respond with an error to a
request that threw an exception, but will not crash the server itself.
This PR does not enable this behavior by default, but makes it
user-configurable.
Also fixes#19109
Also (I think) fixes#7434
## Solution
Instead of rethrowing panics, hand them to the `FallbackErrorHandler`.
If the panic was thrown by an error handler in the first place, we don't
need to pass it back to a handler again. I've added a way for the error
handler to signal that it's the source of the panic.
The constructed error is created without a backtrace, as the default
panic handler already prints it when instructed to via
`RUST_LIB_BACKTRACE`/`RUST_BACKTRACE`.
Panics will not be turned into errors on `no_std` projects.
Potential work for a future PR:
- if a error handler has been specified with e.g. `queue_handled`, use
this error handler instead of the fallback error handler
- if a command panics, still apply the remaining commands in the buffer?
## Testing
See added `panic_to_error` test
---------
Co-authored-by: Gonçalo Rica Pais da Silva <bluefinger@gmail.com>
## Objective
Progress #19024 (removing UUID handles). Even if UUID handles end up
sticking around, there's an argument for deprecating
`AssetId::invalid()` in the name of simplicity and robustness.
## Solution
Remove the last remaining case of `AssetId::invalid()` in the
`custom_phase_item` example. The example only used it as a placeholder
value, so `AssetId::default()` is fine.
```diff
Opaque3dBinKey {
- asset_id: AssetId::<Mesh>::invalid().untyped(),
+ asset_id: AssetId::<Mesh>::default().untyped(),
}
```
## Testing
```sh
cargo run --example custom_phase_item
```
# Objective
Allow multiple cameras to render an atmosphere enabling split screen and
VR apps to use the feature
## Solution
Use iter().next() instead of single() for when multiple Atmosphere
entities exist
## Testing
Tested this by adding a second camera to the atmosphere example.
Tested it in VR with the bevy_oxr crate
# Objective
The text input widget selects the word under the pointer on a double
click. This isn't correct, the select word edit should be queued on the
press following a click. This allows for users to select a word with a
double press and then hold the button and drag to extend the selection.
## Solution
Track multi-presses as well as multi-clicks:
- Add a `count` field to `Press` events.
- Update the click count in `PointerButtonState::clicking` on
consecutive presses within the multi-click duration, not on releases.
- Filter the `clicking` map in both the `Press` and `Click` dispatchers.
- Removed the `MULTI_CLICK_DURATION` const.
- The multi-click duration can now be set using a new
`multi_click_duration` field on the `PickingSettings` resource.
## Testing
```
cargo run --example multiline_text_input
```
You should observe that dragging after selecting a word with a double
press extends the selection.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Allow UI elements to be positioned relative to the viewport rather than
their parent element.
Fixes#9564
## Solution
- New UI marker component `FixedNode`. Requires `Node` and
`OverrideClip`.
- `FixedNode` entities are treated as UI roots in layout, even if they
have a parent.
- `FixedNode`s don't inherit their parent's layout, clipping or
transform context.
- During the taffy layout updates children with `FixedNode` are skipped.
- Added a couple of basic helper functions to `UiSurface`, mainly there
to make the tests a little less painful.
- Added a fairly comprehensive range of new tests, including tests with
`GhostNode`s.
- In the Taffy layout (stored in `UiSurface`) there is nothing to
distinguish `FixedNode`s and root nodes, so they are treated identically
during updates.
--
The original suggestion was to implement it as a `PositionType::Fixed`
variant that could used with `Node`, but I think that would be much more
complicated without support from Taffy. Being able to just directly
query for and filter out `FixedNode` entities directly makes the
implementation much simpler and more efficient.
## Testing
Basic example which shows events bubbling up to the parent from the
fixed node:
```
cargo run --example fixed_node
```
There are also a number of new tests in the `layout` module.
```
cargo test -p bevy_ui --lib --features ghost_nodes
```
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Addresses
[comments](https://github.com/bevyengine/bevy/pull/7317#issuecomment-2143075852)
regarding #7317 (note that this doesn't replace #7317, there are still
some great improvements there besides this syntactical problem).
There currently exist some "special" type data registrations that can be
registered like other type data (e.g. `#[reflect(Hash)]`) or can use a
"special" syntax to allow specifying custom implementations (e.g.
`#[reflect(Hash(custom_hash_fn))]`). And there may be more to follow
(#13432).
What's interesting is that most of these special cased registrations
don't actually come with any type data type. Instead, they simply modify
methods on `Reflect` (e.g. `Reflect::reflect_hash`).
#7317 sought to distinguish between these "special" registrations by
making them lowercase and use a more conventional attribute style:
`#[reflect(hash = "custom_hash_fn")]`.
However, while this did help distinguish these registrations and make
them a bit prettier, they now require the user to actually know which
traits are "special" and which are not (as pointed out
[here](https://github.com/bevyengine/bevy/pull/7317#issuecomment-2143075852)).
Ideally, users shouldn't have to know which traits are "special" until
they need to. For most users, they should just know that they need to
register their trait in order for certain things to work. And the
special-casing may be easier to follow if we open up the configuration
abilities to _all_ type data.
## Solution
This PR introduces `CreateTypeData` which replaces `FromType`. This was
done for two reasons.
Firstly, `FromType` isn't very descriptive as to what it should be used
for. We are creating type data from a type, but it's not immediately
clear this is even for type data. Renaming to `CreateTypeData` should
hopefully make this much clearer.
Secondly, in order to support type data with parameters like the
`custom_hash_fn` in `reflect(Hash(custom_hash_fn))`, an additional
`Input` type parameter had to be added. This makes the new signature
`CreateTypeData<T, Input = ()>`.
We can now create type data that accepts input!
```rust
trait Combine {
fn combine(a: f32, b: f32) -> f32;
}
#[derive(Clone)]
struct ReflectCombine {
multiplier: f32,
additional: f32,
combine: fn(f32, f32) -> f32,
}
impl ReflectCombine {
pub fn combine(&self, a: f32, b: f32) -> f32 {
let combined = (self.combine)(a, b);
let multiplied = self.multiplier * combined;
multiplied + self.additional
}
}
impl<T: Combine + Reflect> CreateTypeData<T, (f32, f32)> for ReflectCombine {
fn create_type_data(input: (f32, f32)) -> Self {
Self {
multiplier: input.0,
additional: input.1,
combine: T::combine,
}
}
}
```
And then register them with the special function-like syntax:
```rust
#[derive(Reflect)]
#[reflect(Combine(2.0, 4.0))]
struct Foo;
```
The above code will compile into the following registration:
```rust
registration.insert(<ReflectCombine as CreateTypeData<Self, _>>::create_type_data((2.0, 4.0)))
```
Notice how the macro automatically generates the tuple for us, so we
don't have to add an additional layer of parentheses.
### Multiple Input Types
You might be wondering why we're using a type parameter instead of an
associated type to specify the input type.
An associated type would limit us to a single implementation. This means
that if we want to support the type data with optional parameters (e.g.
support both `Hash` and `Hash(custom_hash_fn)`), then all type data must
take in `Option<Self::Input>`, regardless of whether or not a `None`
case is supported.
This is important because the macro has to be pass in _something_,
whether that be `()` or `None`.
By using a type parameter we open the door to type data with required
input:
```rust
// `ReflectMyTrait` must be registered with input
impl<T> CreateTypeData<T, u32> for ReflectMyTrait {
fn create_type_data(input: u32) -> Self {
Self {
value: input,
}
}
}
// And we can support all different input types
impl<T> CreateTypeData<T, i32> for ReflectMyTrait {
fn create_type_data(input: i32) -> Self {
Self {
value: input.abs() as u32,
}
}
}
```
However, this may be something we don't necessarily care about since
users could also get away with this using custom input enums. And the
required-input case could be deferred until runtime (i.e. maybe a panic
in the `None` case).
### Adding `ReflectPartialEq` and `ReflectHash`
I had originally considered adding `ReflectPartialEq` and `ReflectHash`
type data to further decrease the differences between the "special"
registrations and the regular ones. However, I chose not to do that to
(1) reduce the complexity of this PR and (2) we may end up removing
these entirely due to #8695.
### What else is this good for?
Another question you might have is what else this is good for beyond
just making things a bit more consistent.
I'm not sure exactly how the community will use it, but I can see it
being used for things like feature gating certain functionality:
```rust
#[derive(Reflect)]
#[cfg_attr(feature = "debug", reflect(MyTrait(true)))]
#[cfg_attr(not(feature = "debug"), reflect(MyTrait(false)))]
struct Foo;
```
Or to emulate specialization via reflection:
```rust
impl<T> DoSomething for T {
fn do_something(&self) {
println!("Doing the same old stuff.");
}
}
#[derive(Reflect)]
#[reflect(ReflectDoSomething(|_| {
println!("Doing something special!");
}))]
struct Foo;
```
Note that all of the above could always be done with manual
registration. However, due to them requiring input, some cases could
_only_ be done with manual registration.
This PR mainly opens the door to doing more of this interesting stuff
with type data via the macro registration. It not only unifies "special"
and regular registrations, but also manual and automatic registrations.
## Testing
The tests for this feature are split into doctests (for the docs on
`CreateTypeData`) and in the compile-fail tests.
These will both be verified automatically by CI.
---
## Changelog
- Replaced `FromType<T>` with `CreateTypeData<T, Input = ()>`
- Type data may now opt-in to accepting input during creation using the
`#[reflect(MyTrait(...))]` syntax
- Added `TypeRegistry::register_type_data_with` method
## Migration Guide
`FromType<T>` has been replaced by `CreateTypeData<T, Input = ()>`.
Implementors of `FromType<T>` will need to update their implementation:
```rust
// BEFORE
impl<T> FromType<T> for ReflectMyTrait {
fn from_type() -> Self {
// ...
}
}
// AFTER
impl<T> CreateTypeData<T> for ReflectMyTrait {
fn create_type_data(input: ()) -> Self {
// ...
}
}
```
Additionally, any calls made to `FromType::from_type` will need to be
updated as well:
```rust
// BEFORE
<ReflectMyTrait as FromType<Foo>>::from_type()
// AFTER
<ReflectMyTrait as CreateTypeData<Foo>>::create_type_data(())
```
See release notes for rationale and usage.
Fixes#17644, #20524
(It may not be a 100% fix, but it's good enough to close the ticket I
think.)
## Testing
- Manual testing with VoiceOver on MacOS (note that VoiceOver /
AccessKit does not work well without `desktop_app` being set, but this
causes other issues)
---------
Co-authored-by: Richard Braakman <rebraakman@gmail.com>
# Objective
- Fixes#24257
## Solution
- `RenderGraph` is already pub, but it moved locations. Specify where in
the migration guide.
- Added `RenderGraph` to `bevy_render`’s prelude. This can be removed if
desired.
## Testing
- ci
# Objective
Enables the resource derive macro to specify the attributes for
- hooks (fixes#24159)
- immutability (fixes#24166)
Also fixes `resource_scope` overwriting change ticks and a few related
doc comments.
## Showcase
```rust
#[derive(Resource)]
#[component(immutable)]
#[component(on_insert = update_level, on_discard = update_level)]
struct Level(i32);
```
The release is nigh! One of the most important tasks is to get the
release notes ready.
I've done them all in a single batch to avoid endless spam and to be
able to consider them in context.
Two of them are deliberately omitted: the BSN notes ( @cart has claimed
these) and render-graph-as-systems ( for @tychedelia).
Key things I focused on when editing and writing these release notes:
- what is worth teaching about, and how much detail does it need
- how do we contextualize and motivate each feature
- is the usage clearly demonstrated without taking up too much space
- the tone should be fun, clear, and optimistic throughout
---------
Co-authored-by: Trashtalk217 <trashtalk217@gmail.com>
Co-authored-by: Adam__ <67244228+Based-A@users.noreply.github.com>
# Objective
- Fixes#23183
- The issue describes strobing that happens on OOM’s (that used to just
crash the application). I’ve also seen strobing happen on validation
errors while some examples were in a broken state very recently. I
believe it’s cause OOM’s and Validations are ignored, which just leads
for rendering to resume and hit those same issues again. The looping is
somehow causing the flashing. To protect against this strobing, I think
the default render error policy needs to be changed.
## Solution
- Instead of ignoring any errors and continuing to render, this PR
changes the default error policy to quit the application upon any error.
OOM and Validation error induced strobing will not happen cause the app
should just quit. I’m not aware of strobing that could happen on
DeviceLost / Internal errors, but I’m leaning towards safety here. You
can correct me (within reason) if there are some obvious errors we
should handle with a different error policy, but I think this is a
better starting point than `Ignore` for all.
- Note: At times, even with this code, I’ve seen the app flash to a
magenta/pink color before exiting (not a strobing pink that would happen
if the app ignored validation errors, just a single abrupt switch before
app exit), so I’m wondering if it’s better to throw a `panic!` rather
than try to quit gracefully.
## Testing
- Fortunately, the `pccm` example and `light_probe_blending` examples
are still broken on `main`.
- `cargo r --example light_probe_blending --features free_camera,https`
correctly closes the app upon Validation error
- `cargo run --example pccm --features="free_camera https”` does the
same.
<details>
<summary>Example console logs</summary>
```
cargo r --example light_probe_blending --features free_camera,https
Compiling bevy v0.19.0-dev (/Users/kchen/CodingProjects/bevy)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 11.24s
Running `target/debug/examples/light_probe_blending`
2026-05-05T01:04:33.028208Z INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "macOS 26.4.1", kernel: "25.4.0", cpu: "Apple M4", core_count: "10", memory: "16.0 GiB" }
2026-05-05T01:04:33.031189Z WARN bevy_asset::io::web: WebAssetPlugin is potentially insecure! Make sure to verify asset URLs are safe to load before loading them. If you promise you know what you're doing, you can silence this warning by setting silence_startup_warning: true in the WebAssetPlugin construction.
2026-05-05T01:04:33.448175Z INFO bevy_render::renderer: AdapterInfo { name: "Apple M4", vendor: 0, device: 0, device_type: IntegratedGpu, device_pci_bus_id: "", driver: "", driver_info: "", backend: Metal, subgroup_min_size: 4, subgroup_max_size: 64, transient_saves_memory: true }
2026-05-05T01:04:34.315115Z INFO bevy_pbr::cluster: GPU clustering is supported on this device.
2026-05-05T01:04:34.315226Z INFO bevy_render::batching::gpu_preprocessing: GPU preprocessing is fully supported on this device.
2026-05-05T01:04:34.611394Z INFO bevy_winit::system: Creating new window Bevy Light Probe Blending Example (65v0)
2026-05-05T01:04:37.312262Z ERROR bevy_render::error_handler: Caught rendering error: Validation Error
Caused by:
In Device::create_bind_group, label = 'mesh_view_bind_group_binding_array'
Number of bindings in bind group descriptor (3) does not match the number of bindings defined in the bind group layout (0)
2026-05-05T01:04:37.900677Z ERROR bevy_render::error_handler: Quitting the application due to Validation RenderError
2026-05-05T01:04:37.964583Z WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-05-05T01:04:37.964628Z WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
...
```
</details>
There is some `WARN bevy_ecs::world::command_queue: CommandQueue has
un-applied commands being dropped. Did you forget to call
SystemState::apply?` spam (like at least 20+ lines of it) after quitting
the application; if anyone knows if that could be prevented somehow, I’m
keen to learn.
---------
Co-authored-by: atlv <email@atlasdostal.com>
# Objective
Fixes
https://github.com/bevyengine/bevy/issues/24084#issuecomment-4365309756
Also fixes `EnvironmentMapLight::rotation` if it is attached to a view.
## Solution
Store view environment map rotation in `LightProbesUniform` and remove
`EnvironmentMapUniform`
## Testing
The examples works:
```
cargo r --example pccm --features free_camera,https
cargo r --example light_probe_blending --features free_camera,https
cargo r --example rotate_environment_map --features pbr_multi_layer_material_textures
```
# Objective
Get ready for a release.
On a personal note, this is a lot easier than doing a showcase pass, as
I really don't know how to hit the right tone for that.
# Objective
Alternative to #24004.
https://github.com/bevyengine/bevy/pull/23288 adds ltc luts for rect
light support which implicitly requires `bevy_image/ktx2` and
`bevy_image/zstd` otherwise loading ltc luts will panic.
We either accept to always enable area light supoort (#24004), or add a
feature to opt out it (this PR).
## Solution
Gate ltc luts behind a feature and merge them to a texture array.
## Testing
`rect_light` example works.
---------
Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
# Objective
An existing migration guide + release note got clobbered by the
`bevy_scene` -> `bevy_ecs_serialization` rename.
Fixes#24007.
## Solution
Carefully integrate the rename into the text to make the migration less
confusing.
# Objective
> With the introduction of scene components, the feathers API has now
reached its final form. This has a number of consequences:
> - There's no longer any reason to keep the experimental flag around
> - The effort of migrating the Bevy examples to feathers widgets is
unblocked, however I think we should start with migrating just one
example
- @viridia
## Solution
1. Rename the feature flag.
2. Write a migration guide.
This was previously attempted as part of
https://github.com/bevyengine/bevy/pull/22934, but I got pushback there.
Now that bsn! is more complete (scene components!) things are better.
I've opted not to change the default features here, even though it makes
it much harder to move our examples over, because that's a much more
contentious change. While I feel that Bevy's default features should be
example-oriented, that's still up for debate!
# Objective
Fixes#23627.
`MeshPipelineViewLayoutKey` uses too many bindings even if features like
ssr, environment map are unused.
## Solution
Don't pre-allocate every combination that grows exponentially. Instead,
create mesh view bind group layout on demand so that we can add more
view keys to reduce unused bindings.
`MeshPipelineViewLayouts::get_view_layout` will be slower, but I'm not
sure how slow it is. My feeling is that the overhead is not high,
compared to when we clone it before.
## Testing
```
WGPU_SETTINGS_PRIO=webgl2 cargo r --example 3d_scene
cargo r --example ssr --features bluenoise_texture
cargo r --example ssao
cargo r --example irradiance_volumes
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
First pass on the release notes. I generated a couple of images for the
post-processing effects and the parallax correction, and I fixed a
couple of small mistakes. I also removed two release notes because I
don't think they were necessary.
This reverts commit 98c69104c4.
# Objective
@cart's
[objections](https://discord.com/channels/691052431525675048/749335865876021248/1499119335828881529):
1. I'm not sure top-level World methods for path-driven access is
justifiable in its current form. For anything but the smallest
application, looping over every Name in the app to find the one you're
looking for is pretty much never what you want
2. Calling Name::to_string() and collecting the result in a Vec isn't
going to cut it performance wise. Even within the scoped context of
entity access we shouldn't be doing any allocations or "recomputations".
Just the act of comparison across all entities is arguably too expensive
## Solution
Revert it for now and go back to revising the work before landing it.
## Plan going forward
[Core
plan](https://discord.com/channels/691052431525675048/749335865876021248/1499120012034838692):
1. An EntityRef API that is always relative to the current entity
2. Grab the Children component.
3. Iterate over it, read the Name on each component, see if it matches
the current piece of the path.
4. Add docs discouraging usage in most cases; users should prefer
`EntityTemplate` and pre-resolved connections.
As discussed
[here](https://discord.com/channels/691052431525675048/749335865876021248/1499120218927271987),
opt-in names indexing (both all names for inspector use cases and based
on With filters for e.g. animation) should be considered too, but
doesn't need to be in the MVP. @laundmo suggests the use of a trie,
while Sander says that a hashmap is adequate in flecs.
# Objective
Fixes: #16201
Originally based on and closes: #20848
## Solution
Add two to methods to world
```rust
fn get_entity_from_path<R: Relationship, C: Component + PartialEq + Clone>(
&self,
root: Option<Entity>,
path: &[C],
) -> Option<Entity>;
fn get_path_from_entity<R: Relationship, C: Component + PartialEq + Clone>(
&self,
root: Option<Entity>,
entity: Entity,
) -> Option<Vec<C>>;
```
Original methods were taken from #20848, but changed to focus on bottom
up search instead of a top down search, which should hopefully be more
efficient and made them generic over the relationship.
Also added an `EntityPath` variant to `EntityTemplate`.
## Testing
Tests included and working.
## Showcase
```rust
#[derive(Component)]
#[relationship(relationship_target = WieldedBy)]
pub struct Weapon(pub Entity);
#[derive(Component)]
#[relationship_target(relationship = Weapon)]
pub struct WieldedBy(Entity):
fn player() -> impl Scene {
bsn! {
Player
Weapon("Items/Weapons/Sword")
}
}
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
android handle only one activity, either game or native. To use native,
the default could now be used, but all other android need to add the
game activity.
# Objective
android native activities should be able to use the default
## Solution
remove android-game-activity from default
## Testing
created a default app for android native activity with default
## Important
Will be a breaking change for apps using android-game-activity.
---------
Co-authored-by: Christian Oelschlegel <oelschle@sciphy.de>
Co-authored-by: leomeinel <leo@meinel.dev>
# Objective
- Followup to #23663.
- Make the nested loading API easier to understand (when reading from
docs.rs).
## Solution
- Remove the type-state stuff from `NestedLoader`. Make each way of
calling load just be its own function.
- Add override_unapproved to the `NestedLoader`.
- Rename `NestedLoader` to `NestedLoadBuilder` (to match `LoadBuilder`).
- Rename `.loader()` to `.load_builder()` just like
`AssetServer::load_builder`.
Some decisions:
- I included `override_unapproved` to match LoadBuilder. We could remove
this to say "loaders shouldn't be able to override", but I don't think
that's problematic, and I'd rather have it for completeness.
- I omitted `with_guard` from `NestedLoadBuilder`. This option doesn't
really make sense, plus you could just use the async methods to do this
instead.
- Since we omitted the `with_guard` (since it really doesn't make sense
in this context), I decided not to reuse `LoadBuilder` in any way here.
Either way, we need to deal with dependencies, so we couldn't just use
it directly anyway.
- I just created all 9 load variants as separate methods. That's a lot
compared to the 4 variants in LoadBuilder. We could reduce it to 6 load
variants, if we kept the `with_reader` stuff as a type-state like we did
with `NestedLoader`. I'm not a fan of how many variants we have, but I
don't think there's a way to do this without opening up impossible
configurations, like doing with_reader for a deferred call - this
doesn't work!
# Objective
Fixes#23781
When Hdr is on, the order of `tonemapping` and
`fullscreen_material_system` is uncertain and the result of main pass
can be cleared. `run_in` `run_before` `run_after` can only specify
system set and can't specify a specific system and them hardcode
`Core3dSystems`.
## Solution
Replace `run_in` `run_before` `run_after` with `fn
schedule_configs(system: ScheduleConfigs<BoxedSystem>) ->
ScheduleConfigs<BoxedSystem>`
## Testing
Run fullscreen_material example with Hdr
We discovered in Processing when using GLFW as a windowing backend that
sometimes a window is reported as occluded for the first several frames.
When a camera is configured with `ClearColorConfig::None`, this can be
problematic as the first several frames are skipped and thus do not
accumulate drawing state in the `ViewTarget` texture. In general, when
the user requests a color attachment with `Load` semantics, we should do
our best to make sure that we write into that texture, as
missing/dropped frames my result in visually incorrect output.
A few related fixes:
1. Make `out_texture` and `Option` in `ViewTarget`. A valid render
target texture is *not* required in order to render if the user has
requested view target `Load` semantics. We still skip rendering when the
user has requested `Clear` as this output will be overwritten anyway,
saving some energy (i.e. preserves the previous behavior).
2. Skip acquiring the swapchain if no camera is configured to
`CameraOutputMode::Write` to a swapchain. This should help avoid
weirdness where we acquire but don't do work on a swapchain texture.
# Objective
This prefix was ruled unhelpful, and we previously removed it from most
of our types.
We missed a few apparently (spotted in #23924).
## Solution
Find and replace + update the migration guide.
# Objective
`bevy_transform`'s multi-threading behavior was previously gated on
`feature = "std"`, which incorrectly conflated standard library
availability with multi-threading capability. This means:
- Users who enabled `no_std` but had multi-threading available lost
parallelism unintentionally.
- Users who had `std` but wanted single-threaded execution (e.g. WASM)
still attempted to use the parallel path.
- `bevy_internal`'s `multi_threaded` feature did not propagate down to
`bevy_transform`, so the parallel implementation was not activated when
expected.
- The `bevy_log` dependency was pulled in unconditionally via the `std`
feature, when the only use was optional trace-level warnings.
## Solution
- Added an explicit `multi_threaded` feature to `bevy_transform`, backed
by `bevy_tasks/multi_threaded`, matching the pattern used by `bevy_ecs`
and other crates.
- Replaced all `#[cfg(feature = "std")]` / `#[cfg(not(feature =
"std"))]` guards on parallel/serial code paths with
`#[cfg(all(not(target_arch = "wasm32"), feature = "multi_threaded"))]` /
`#[cfg(any(target_arch = "wasm32", not(feature = "multi_threaded")))]` —
the same guard pattern already used in `bevy_ecs`.
- Added `bevy_transform/multi_threaded` to `bevy_internal`'s and
`bevy_render`'s `multi_threaded` feature lists so parallel transforms
are activated end-to-end when building Bevy with `multi_threaded`.
- Removed the `bevy_log` dependency from `bevy_transform`. Its sole
usage was a `warn_once!` inside `propagate_transforms_for`. This is
replaced with a direct `tracing::warn!` wrapped in `bevy_utils::once!`,
gated on a new opt-in `trace` feature (`dep:tracing`). This keeps the
default build lighter.
- Inlined the `trace` feature spans in `mark_dirty_trees` that
previously depended on `bevy_log`'s re-exported `tracing`, now using
`tracing` directly under `#[cfg(feature = "trace")]`.
## Testing
- All existing unit tests in `bevy_transform::systems::test` pass
unchanged — no behavioral changes, only cfg guard corrections.
- No performance regressions observed by
[Benchmark](https://github.com/bevyengine/bevy/pull/23906).
---
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
# Objective
Part of #19236
## Solution
A numeric text input widget. This includes:
* Colored stripes ("sigils") for designating X, Y and Z input fields.
* Character filtering
* ValueChange events
Not supported:
* Validation error events
* Range and precision options
Additional changes in this PR:
* Added small labels (related to text inputs only insofar as numeric
inputs have small labels above them in the editor design).
* Refactored labels to be simple spans instead of nested entities - this
required a non-inherited text font theme component, so we renamed the
existing component to have the word "inherited" in the name.
* Consolidated the way focus outlines work for text inputs and other
widgets
* I had to make changes to Slider and other widgets to support the
`is_final` flag in `ValueChange`. This is necessary to allow listeners
the choice between spammy and not-spammy updates when listening to
widget outputs.
## Testing
Manual testing
## Showcase
<img width="353" height="101" alt="numeric_input"
src="https://github.com/user-attachments/assets/36efd243-fa01-484e-bab5-689aa6be4d9e"
/>
# Objective
According to #18900 , rename `MeshPipelineSet` to `MeshPipelineSystems`
for a consistent naming convention.
`MeshPipelineSet` was introduced in this cycle, so no migration guide
required.
## Testing
```
cargo run --example specialized_mesh_pipeline
```
# Objective
Split off the stack index field from `ComputedNode` into its own
specialized component.
* Makes for a cleaner UI schedule and a clearer division of
responsibilities.
* More fine-grained change detection, might allow for some new
optimisations.
Fixes#23862
## Solution
* Remove the `stack_index` field and its accessor from `ComputedNode`.
* New component `ComputedStackIndex`, newtyping a `u32`.
* Require `ComputedStackIndex` on `ComputedNode`.
* In `ui_stack_system`, query for and update `ComputedStackIndex`
instead of `ComputedNode`.
* In rendering add queries for `ComputedStackIndex`.
* Removed the ambiguity supression for `ui_stack_system`, as no longer
needed.
Notes
* `ui_stack_system` should probably replace its `With<Node>` query
filters with `With<ComputedStackIndex>`. It uses the ghost hierarchy
navigation params though, which complicates things, so I left them alone
here.
* In the future it might make sense to make the value optional, with
`None` indicating the UI node was removed from the layout via
`Display::None`.
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Fixes https://github.com/bevyengine/bevy/issues/23814
It isn't clear whether we should be detailing what is now possible with
bevy settings (can use enums and tuple structs) in the release notes so
i've left it as is but happy to collate code examples and the toml they
result in and add this.