# 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
- 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
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
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(())
```
# 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);
```
# 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
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>
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
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
- In 0.18, we had 10 different functions that load assets (I'm not even
counting `load_folder`).
- In 0.19, we've even added `load_erased` - but it unfortunately doesn't
support all the features that the other variants support.
- We apparently needed `load_acquire_with_settings_override` which 1)
loads the asset, 2) uses the settings provided, 3) allows reading
unapproved asset paths, and 4) drops a guard once the load completes.
- That's fine if that's necessary. But we needed to create an explicit
variant for that.
- We need fewer load paths!
## Solution
- Create a builder.
- Store all these options dynamically instead of statically handling
each case.
- Have the caller choose a particular "kind" of load when they are
ready: `load`, `load_erased`, `load_untyped`, or `load_untyped_async`.
- I intentionally didn't provide a `load_async` or `load_erased_async`,
since those can be replicated using `load`/`load_erased` +
`AssetServer::wait_for_asset_id` to get the exact same effect.
I am also intentionally leaving `NestedLoader` untouched in this PR, but
a followup will duplicate this API for `NestedLoader`, which should make
it easier to understand.
Unlike the `NestedLoader` API, we aren't doing any type-state craziness,
so the docs are much more clear: users don't need to understand how
type-state stuff works, they just call the handful of methods on the
type. The "cost" here is we now need to be careful about including the
cross product of loads between static asset type, runtime asset type, or
dynamic asset type, crossed with deferred or async. In theory, if we
added more kinds on either side, we would need to expand this cross
product a lot. In practice though, it seems unlikely there will be any
more variants there. (maybe there could be a blocking variant? I don't
think this is a popular opinion though).
A big con here is some somewhat common calls are now more verbose.
Specifically, `asset_server.load_with_settings()` has become
`asset_server.load_builder().with_settings().load()`. I am not really
concerned about this though, since it really isn't that painful.
## Testing
- Tests all pass!
---
## Showcase
Now instead of:
```rust
asset_server.load_acquire_with_settings_override("some_path", |settings: &mut GltfLoaderSettings| { ... }, my_lock_guard);
```
You can instead do:
```rust
asset_server.load_builder()
.with_guard(my_lock_guard)
.with_settings(|settings: &mut GltfLoaderSettings| { ... })
.override_unapproved()
.load("some_path");
```
We also now cover more variants! For example, you can now load an asset
untyped with a guard, or with override_unapproved, etc.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- Clean up our texture format handling.
- Fix#23732
- Get us closer to #22563
- It makes no sense for views to talk about being hdr or not. They have
a texture format, that's it. What does HDR shadows even mean lol
- Same for compositing_space
## Solution
- Remove ExtractedView::hdr
- Add ExtractedView::texture_format
- Move ExtractedView::compositing_space to
ExtractedCamera::compositing_space
- Add texture_format to a bunch of specialization keys instead of hdr
bool
- Convert VolumetricFogPipelineKey to not use flags and just use bool
and texture format
- Remove BevyDefault TextureFormat
- Remove ViewTarget TEXTURE_FORMAT_HDR
## Testing
- Pretty extensively test at this point
This has a migration guide.
---------
Co-authored-by: Willow Black <wmcblack@gmail.com>
Co-authored-by: Máté Homolya <mate.homolya@gmail.com>
Co-authored-by: Luo Zhihao <luo_zhihao@outlook.com>
Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
# Objective
Names beginning in `try_` (i.e. `Font::try_from_bytes` in this case)
imply that they are fallible - that is, they return an `Option` or
`Result` if the constructor fails while checking invariants. This method
used to return a `Result`, but no longer does after the recent change to
using `parley`.
## Solution
This PR renames the method to `from_bytes` to convey that it's now
infallible.
## Testing
I switched to my fork locally for my game, and it works with the new
name. There isn't much to test here.
However, this _is_ technically a breaking change to the API (but so is
the existing change on the main branch, so it should be fine).
# Objective
Fix#11874.
## Solution
Old me said that he had problems fixing this, but I just changed the
exit systems to `Last` and I have encountered no issues.
## Testing
CI passes.
I ran several examples including `multiple_windows` and I did the same
changes to 0.18.1 and tested it with my library, I found no errors while
closing the window in both cases.
---------
Co-authored-by: shunkie <shunkie@qq.com>
# Objective
`FeathersPlugin` is extremely similar to `FeathersPlugins`. This is
particularly bad because they can both be added via `.add_plugins`.
Fixes#21213.
## Solution
Rename to `FeathersCorePlugin`. It's a perfectly inoffensive name, and
will not be accidentally confused.
# Objective
When working with more complex widgets, users have repeatedly asked for
a way to reliably detect when an entity gains or loses focus. They have
not, however, filed an issue about this 🎟️
These are called
[`focus`](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event)
/
[`focusin`](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusin_event)
+
[`blur`](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event)
/
[`focusout`](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusout_event)
on the web. The former element in these pairs does not bubble, while the
latter does.
Because Bevy's observer infrastructure allows you to get the original
target of a bubbled event, we can create only bubbling variations.
@viridia has suggested that
https://github.com/bevyengine/bevy/pull/23707 would benefit from a form
of this feature.
## Rejected designs
The simplest approach would be to simply add a `previously_focused`
field on `InputFocus`, and then emit events based on the difference
using an ordinary system. Unfortunately, this has a serious flaw: events
are lost if this changes state multiple times in the same frame.
We can resolve this problem by completely locking down access, and
requiring commands or events to be used to change the input focus. This
ensures no changes are lost and sends them off semi-immediately, but is
a major breaking change to this API and prevents immediate-mode checks
of "what is the current input focus".
## Solution
We can do better. If we sacrifice "FocusGained and Lost must be emitted
immediately", we can track changes inside of `InputFocus`, before
sending them off in an ordinary system.
This is minimally breaking (you have to use the getters/setters now),
and ensures no gained/lost events are ever missed. Users who completely
overwrite `InputFocus` (e.g. by using `from_entity`) will miss changes,
but frankly, you deserve it if you ignore the nice setters and clear
warnings in the docs.
- Define `FocusGained` and `FocusLost` events. Split to their own file
for cleanliness.
- Lock down access to `InputFocus`, forcing users to always go through
the existing getters and setters.
- Modify `InputFocus` to track changes as they have been made.
- Add a system to drain these once-per-frame in `PostUpdate`, converting
them into `FocusGained` and `FocusLost`.
- This timing helps ensure that any user / library code that changes the
focus has time to run, but rendering code that relies on accurate focus
information to display widgets has the information available.
- I could not find existing systems in `PostUpdate` that this needed a
relative ordering for.
- Create an `InputFocusPlugin` to store this new system, stealing some
of the setup that was previously in `InputDispatchPlugin`. Somewhat
incidentally, this fixes#19057, by selecting option 1.
## Testing
I was *not* very confident that my implementation of this logic was
correct, so I wrote a rather aggressive set of mid-level tests, using
`App`.
They pass, so apparently my first implementation was actually good
enough.
# Objective
- Partially addresses #23688.
- Prevents use of `Skybox::default()` from causing errors.
## Solution
`Skybox::default()` is problematic because it contains an `Image` that
is not a valid skybox. ~~This change removes the `Default`
implementation and instead provides a `new()` function which takes the
image as a parameter (and also the brightness, which is practically
required).~~ This change makes the `image` field optional so that the
default `None` renders nothing.
Things we could do instead of this:
* Make `Skybox` not implement `Default`. I am informed this is a bad
idea.
* Create a default cubemap image for `default()` to use.
## Testing
Ran the `skybox` and `irradiance_volumes` examples.
# 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?
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.
# 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>
# 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>
# Objective
`DefaultErrorHandler` is a confusing name.
While it is the error handler "used by default" if no alterantive is
provided, it should not be your default choice. Local error handling is
usually better than simply calling `?`.
Moreover, it's quite hard to talk about "the default configuration of
the `DefaultErrorHandler`" in the docs, and very weird to discuss
modifying the default error handler.
## Solution
Rename it to `FallbackErorrHandler`.
Update the docs to match.
This is a better name, since it reflects this pattern's "safety net"
usage, without being confusing to discuss.
Add a deprecated type alias and a migration guide to be nice to users.
EDIT: apparently also mention the new Severity logic in the error
handling example.
---------
Co-authored-by: Mira <specificprotagonist@posteo.org>
Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
# Objective
Fix the text overflow bug seen in the `text_debug` example:
<img width="1924" height="1127" alt="Screenshot 2026-03-29 120536"
src="https://github.com/user-attachments/assets/699baf3d-7f0c-4224-8f7d-d55cb05c6eab"
/>
## Solution
Always assumed this was a bug in Taffy, but realised after working out
the intrinsic sizing needed for `EditableText` that the naive
`TextMeasure` implementation from before didn't respect width
constraints and calculated the width for wrapping based on the
unconstrained available space. This resulted in the text layout
overflowing the node by one line.
* Renamed `MeasureArgs` `width` and `height` to `known_width` and
`known_height`.
* `MeasureArgs` now has two new methods, `resolve_width` and
`resolve_height`. They return the resolved values for the style
constraints along the respective axis and a clamped effective size.
* `Measure::measure` no longer has a `style` parameter. Instead it's
included as a `style` field on `MeasureArgs`.
* Measurement for `ImageNode` and `EditableText` also uses the
`resolve_width` and `resolve_height` methods.
## Testing
```
cargo run --example text_debug
```
## Showcase
<img width="1924" height="1127" alt="Screenshot 2026-03-29 120343"
src="https://github.com/user-attachments/assets/47d764e8-d162-4377-9990-962934b2bb91"
/>
# Objective
Trying to write a `bevy_immediate` widget with the following signature:
```rust
fn meter<T: Display>(self, name: impl Into<String>, current: Ref<T>, max: Ref<T>) -> Self;
```
Where it would be called like so (pseudocode):
```rust
#[derive(Component)]
struct Health {
current: f32,
max: f32
}
fn render(ui: &mut ImmEntity, health: Ref<Health>) {
ui.meter("Health", health.map(|v| &v.current), health.map(|v| &v.max));
}
```
However, this results in a compiler error as there's currently no way to
split borrows on a `Ref<T>` (there is no `Ref::clone`).
## Solution
- Make `Ref` both `Clone` and `Copy`, easily allowing split borrows.
Note: `Res<T>` is not `Clone` or `Copy` to make cloning the `T` out
easier, but comparatively `Ref<T>` is much less commonly used (users
primarily write `&T` in their query). And even further, I believe the
amount of users who both use `Ref<T>` and who need to clone the `T` out
will be quite small (we don't even have a `Ref::clone` inherent
function, if that proves anything).
Alternatively, we could add `Ref::clone` as an inherent function, but
the UX on that isn't great.
## Testing
The rust compiler keeps us in check here.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>