# Objective
after #15156 it seems like using distinct directional lights on
different views is broken (and will probably break spotlights too). fix
them
## Solution
the reason is a bit hairy so with an example:
- camera 0 on layer 0
- camera 1 on layer 1
- dir light 0 on layer 0 (2 cascades)
- dir light 1 on layer 1 (2 cascades)
in render/lights.rs:
- outside of any view loop,
- we count the total number of shadow casting directional light cascades
(4) and assign an incrementing `depth_texture_base_index` for each (0-1
for one light, 2-3 for the other, depending on iteration order) (line
1034)
- allocate a texture array for the total number of cascades plus
spotlight maps (4) (line 1106)
- in the view loop, for directional lights we
- skip lights that don't intersect on renderlayers (line 1440)
- assign an incrementing texture layer to each light/cascade starting
from 0 (resets to 0 per view) (assigning 0 and 1 each time for the 2
cascades of the intersecting light) (line 1509, init at 1421)
then in the rendergraph:
- camera 0 renders the shadow map for light 0 to texture indices 0 and 1
- camera 0 renders using shadows from the `depth_texture_base_index`
(maybe 0-1, maybe 2-3 depending on the iteration order)
- camera 1 renders the shadow map for light 1 to texture indices 0 and 1
- camera 0 renders using shadows from the `depth_texture_base_index`
(maybe 0-1, maybe 2-3 depending on the iteration order)
issues:
- one of the views uses empty shadow maps (bug)
- we allocated a texture layer per cascade per light, even though not
all lights are used on all views (just inefficient)
- I think we're allocating texture layers even for lights with
`shadows_enabled: false` (just inefficient)
solution:
- calculate upfront the view with the largest number of directional
cascades
- allocate this many layers (plus layers for spotlights) in the texture
array
- keep using texture layers 0..n in the per-view loop, but build
GpuLights.gpu_directional_lights within the loop too so it refers to the
same layers we render to
nice side effects:
- we can now use `max_texture_array_layers / MAX_CASCADES_PER_LIGHT`
shadow-casting directional lights per view, rather than overall.
- we can remove the `GpuDirectionalLight::skip` field, since the gpu
lights struct is constructed per view
a simpler approach would be to keep everything the same, and just
increment the texture layer index in the view loop even for
non-intersecting lights. this pr reduces the total shadowmap vram used
as well and isn't *much* extra complexity. but if we want something less
risky/intrusive for 16.1 that would be the way.
## Testing
i edited the split screen example to put separate lights on layer 1 and
layer 2, and put the plane and fox on both layers (using lots of
unrelated code for render layer propagation from #17575).
without the fix the directional shadows will only render on one of the
top 2 views even though there are directional lights on both layers.
```rs
//! Renders two cameras to the same window to accomplish "split screen".
use std::f32::consts::PI;
use bevy::{
pbr::CascadeShadowConfigBuilder, prelude::*, render:📷:Viewport, window::WindowResized,
};
use bevy_render::view::RenderLayers;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(HierarchyPropagatePlugin::<RenderLayers>::default())
.add_systems(Startup, setup)
.add_systems(Update, (set_camera_viewports, button_system))
.run();
}
/// set up a simple 3D scene
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let all_layers = RenderLayers::layer(1).with(2).with(3).with(4);
// plane
commands.spawn((
Mesh3d(meshes.add(Plane3d::default().mesh().size(100.0, 100.0))),
MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
all_layers.clone()
));
commands.spawn((
SceneRoot(
asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")),
),
Propagate(all_layers.clone()),
));
// Light
commands.spawn((
Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
DirectionalLight {
shadows_enabled: true,
..default()
},
CascadeShadowConfigBuilder {
num_cascades: if cfg!(all(
feature = "webgl2",
target_arch = "wasm32",
not(feature = "webgpu")
)) {
// Limited to 1 cascade in WebGL
1
} else {
2
},
first_cascade_far_bound: 200.0,
maximum_distance: 280.0,
..default()
}
.build(),
RenderLayers::layer(1),
));
commands.spawn((
Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
DirectionalLight {
shadows_enabled: true,
..default()
},
CascadeShadowConfigBuilder {
num_cascades: if cfg!(all(
feature = "webgl2",
target_arch = "wasm32",
not(feature = "webgpu")
)) {
// Limited to 1 cascade in WebGL
1
} else {
2
},
first_cascade_far_bound: 200.0,
maximum_distance: 280.0,
..default()
}
.build(),
RenderLayers::layer(2),
));
// Cameras and their dedicated UI
for (index, (camera_name, camera_pos)) in [
("Player 1", Vec3::new(0.0, 200.0, -150.0)),
("Player 2", Vec3::new(150.0, 150., 50.0)),
("Player 3", Vec3::new(100.0, 150., -150.0)),
("Player 4", Vec3::new(-100.0, 80., 150.0)),
]
.iter()
.enumerate()
{
let camera = commands
.spawn((
Camera3d::default(),
Transform::from_translation(*camera_pos).looking_at(Vec3::ZERO, Vec3::Y),
Camera {
// Renders cameras with different priorities to prevent ambiguities
order: index as isize,
..default()
},
CameraPosition {
pos: UVec2::new((index % 2) as u32, (index / 2) as u32),
},
RenderLayers::layer(index+1)
))
.id();
// Set up UI
commands
.spawn((
UiTargetCamera(camera),
Node {
width: Val::Percent(100.),
height: Val::Percent(100.),
..default()
},
))
.with_children(|parent| {
parent.spawn((
Text::new(*camera_name),
Node {
position_type: PositionType::Absolute,
top: Val::Px(12.),
left: Val::Px(12.),
..default()
},
));
buttons_panel(parent);
});
}
fn buttons_panel(parent: &mut ChildSpawnerCommands) {
parent
.spawn(Node {
position_type: PositionType::Absolute,
width: Val::Percent(100.),
height: Val::Percent(100.),
display: Display::Flex,
flex_direction: FlexDirection::Row,
justify_content: JustifyContent::SpaceBetween,
align_items: AlignItems::Center,
padding: UiRect::all(Val::Px(20.)),
..default()
})
.with_children(|parent| {
rotate_button(parent, "<", Direction::Left);
rotate_button(parent, ">", Direction::Right);
});
}
fn rotate_button(parent: &mut ChildSpawnerCommands, caption: &str, direction: Direction) {
parent
.spawn((
RotateCamera(direction),
Button,
Node {
width: Val::Px(40.),
height: Val::Px(40.),
border: UiRect::all(Val::Px(2.)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
},
BorderColor(Color::WHITE),
BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
))
.with_children(|parent| {
parent.spawn(Text::new(caption));
});
}
}
#[derive(Component)]
struct CameraPosition {
pos: UVec2,
}
#[derive(Component)]
struct RotateCamera(Direction);
enum Direction {
Left,
Right,
}
fn set_camera_viewports(
windows: Query<&Window>,
mut resize_events: EventReader<WindowResized>,
mut query: Query<(&CameraPosition, &mut Camera)>,
) {
// We need to dynamically resize the camera's viewports whenever the window size changes
// so then each camera always takes up half the screen.
// A resize_event is sent when the window is first created, allowing us to reuse this system for initial setup.
for resize_event in resize_events.read() {
let window = windows.get(resize_event.window).unwrap();
let size = window.physical_size() / 2;
for (camera_position, mut camera) in &mut query {
camera.viewport = Some(Viewport {
physical_position: camera_position.pos * size,
physical_size: size,
..default()
});
}
}
}
fn button_system(
interaction_query: Query<
(&Interaction, &ComputedNodeTarget, &RotateCamera),
(Changed<Interaction>, With<Button>),
>,
mut camera_query: Query<&mut Transform, With<Camera>>,
) {
for (interaction, computed_target, RotateCamera(direction)) in &interaction_query {
if let Interaction::Pressed = *interaction {
// Since TargetCamera propagates to the children, we can use it to find
// which side of the screen the button is on.
if let Some(mut camera_transform) = computed_target
.camera()
.and_then(|camera| camera_query.get_mut(camera).ok())
{
let angle = match direction {
Direction::Left => -0.1,
Direction::Right => 0.1,
};
camera_transform.rotate_around(Vec3::ZERO, Quat::from_axis_angle(Vec3::Y, angle));
}
}
}
}
use std::marker::PhantomData;
use bevy::{
app::{App, Plugin, Update},
ecs::query::QueryFilter,
prelude::{
Changed, Children, Commands, Component, Entity, Local, Query,
RemovedComponents, SystemSet, With, Without,
},
};
/// Causes the inner component to be added to this entity and all children.
/// A child with a Propagate<C> component of it's own will override propagation from
/// that point in the tree
#[derive(Component, Clone, PartialEq)]
pub struct Propagate<C: Component + Clone + PartialEq>(pub C);
/// Internal struct for managing propagation
#[derive(Component, Clone, PartialEq)]
pub struct Inherited<C: Component + Clone + PartialEq>(pub C);
/// Stops the output component being added to this entity.
/// Children will still inherit the component from this entity or its parents
#[derive(Component, Default)]
pub struct PropagateOver<C: Component + Clone + PartialEq>(PhantomData<fn() -> C>);
/// Stops the propagation at this entity. Children will not inherit the component.
#[derive(Component, Default)]
pub struct PropagateStop<C: Component + Clone + PartialEq>(PhantomData<fn() -> C>);
pub struct HierarchyPropagatePlugin<C: Component + Clone + PartialEq, F: QueryFilter = ()> {
_p: PhantomData<fn() -> (C, F)>,
}
impl<C: Component + Clone + PartialEq, F: QueryFilter> Default for HierarchyPropagatePlugin<C, F> {
fn default() -> Self {
Self {
_p: Default::default(),
}
}
}
#[derive(SystemSet, Clone, PartialEq, PartialOrd, Ord)]
pub struct PropagateSet<C: Component + Clone + PartialEq> {
_p: PhantomData<fn() -> C>,
}
impl<C: Component + Clone + PartialEq> std::fmt::Debug for PropagateSet<C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PropagateSet")
.field("_p", &self._p)
.finish()
}
}
impl<C: Component + Clone + PartialEq> Eq for PropagateSet<C> {}
impl<C: Component + Clone + PartialEq> std:#️⃣:Hash for PropagateSet<C> {
fn hash<H: std:#️⃣:Hasher>(&self, state: &mut H) {
self._p.hash(state);
}
}
impl<C: Component + Clone + PartialEq> Default for PropagateSet<C> {
fn default() -> Self {
Self {
_p: Default::default(),
}
}
}
impl<C: Component + Clone + PartialEq, F: QueryFilter + 'static> Plugin
for HierarchyPropagatePlugin<C, F>
{
fn build(&self, app: &mut App) {
app.add_systems(
Update,
(
update_source::<C, F>,
update_stopped::<C, F>,
update_reparented::<C, F>,
propagate_inherited::<C, F>,
propagate_output::<C, F>,
)
.chain()
.in_set(PropagateSet::<C>::default()),
);
}
}
pub fn update_source<C: Component + Clone + PartialEq, F: QueryFilter>(
mut commands: Commands,
changed: Query<(Entity, &Propagate<C>), (Changed<Propagate<C>>, Without<PropagateStop<C>>)>,
mut removed: RemovedComponents<Propagate<C>>,
) {
for (entity, source) in &changed {
commands
.entity(entity)
.try_insert(Inherited(source.0.clone()));
}
for removed in removed.read() {
if let Ok(mut commands) = commands.get_entity(removed) {
commands.remove::<(Inherited<C>, C)>();
}
}
}
pub fn update_stopped<C: Component + Clone + PartialEq, F: QueryFilter>(
mut commands: Commands,
q: Query<Entity, (With<Inherited<C>>, F, With<PropagateStop<C>>)>,
) {
for entity in q.iter() {
let mut cmds = commands.entity(entity);
cmds.remove::<Inherited<C>>();
}
}
pub fn update_reparented<C: Component + Clone + PartialEq, F: QueryFilter>(
mut commands: Commands,
moved: Query<
(Entity, &ChildOf, Option<&Inherited<C>>),
(
Changed<ChildOf>,
Without<Propagate<C>>,
Without<PropagateStop<C>>,
F,
),
>,
parents: Query<&Inherited<C>>,
) {
for (entity, parent, maybe_inherited) in &moved {
if let Ok(inherited) = parents.get(parent.parent()) {
commands.entity(entity).try_insert(inherited.clone());
} else if maybe_inherited.is_some() {
commands.entity(entity).remove::<(Inherited<C>, C)>();
}
}
}
pub fn propagate_inherited<C: Component + Clone + PartialEq, F: QueryFilter>(
mut commands: Commands,
changed: Query<
(&Inherited<C>, &Children),
(Changed<Inherited<C>>, Without<PropagateStop<C>>, F),
>,
recurse: Query<
(Option<&Children>, Option<&Inherited<C>>),
(Without<Propagate<C>>, Without<PropagateStop<C>>, F),
>,
mut to_process: Local<Vec<(Entity, Option<Inherited<C>>)>>,
mut removed: RemovedComponents<Inherited<C>>,
) {
// gather changed
for (inherited, children) in &changed {
to_process.extend(
children
.iter()
.map(|child| (child, Some(inherited.clone()))),
);
}
// and removed
for entity in removed.read() {
if let Ok((Some(children), _)) = recurse.get(entity) {
to_process.extend(children.iter().map(|child| (child, None)))
}
}
// propagate
while let Some((entity, maybe_inherited)) = (*to_process).pop() {
let Ok((maybe_children, maybe_current)) = recurse.get(entity) else {
continue;
};
if maybe_current == maybe_inherited.as_ref() {
continue;
}
if let Some(children) = maybe_children {
to_process.extend(
children
.iter()
.map(|child| (child, maybe_inherited.clone())),
);
}
if let Some(inherited) = maybe_inherited {
commands.entity(entity).try_insert(inherited.clone());
} else {
commands.entity(entity).remove::<(Inherited<C>, C)>();
}
}
}
pub fn propagate_output<C: Component + Clone + PartialEq, F: QueryFilter>(
mut commands: Commands,
changed: Query<
(Entity, &Inherited<C>, Option<&C>),
(Changed<Inherited<C>>, Without<PropagateOver<C>>, F),
>,
) {
for (entity, inherited, maybe_current) in &changed {
if maybe_current.is_some_and(|c| &inherited.0 == c) {
continue;
}
commands.entity(entity).try_insert(inherited.0.clone());
}
}
```
# Objective
Fixes#19219
## Solution
Instead of calling `world.commands().trigger` and
`world.commands().trigger_targets` whenever each scene is spawned, save
the `instance_id` and optional parent entity to perform all such calls
at the end. This prevents the potential flush of the world command queue
that can happen if `add_child` is called from causing the crash.
## Testing
- Did you test these changes? If so, how?
- Verified that I can no longer reproduce the bug with the instructions
at #19219.
- Ran `bevy_scene` tests
- Visually verified that the following examples still run as expected
`many_foxes`, `scene` . (should I test any more?)
- Are there any parts that need more testing?
- Pending to run `cargo test` at the root to test that all examples
still build; I will update the PR when that's done
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Run bevy as usual
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
- N/a (tested on Linux/wayland but it shouldn't be relevant)
---
# Objective
Fix#19324
## Solution
`EntityCloner` replaces required components when filtering. This is
unexpected when comparing with the way the rest of bevy handles required
components. This PR separates required components from explicit
components when filtering in `EntityClonerBuilder`.
## Testing
Added a regression test for this case.
# Objective
Fixes https://github.com/bevyengine/bevy/issues/17933
## Solution
Correct "value has changed'" in docs to "value has been added or mutably
dereferenced", with a note for emphasis copied from the docs for
Changed.
## Testing
-
# Objective
- Fix a reference in the example usage comments of bevy_utils
Default::default
## Solution
- Just a word change in the comments
## Testing
- No actual code changes to test
# Objective
Adds the ability to restrict playback of an audio source to a certain
region in time. In other words, you can set a custom start position and
duration for the audio clip. These options are set via the
`PlaybackSettings` component, and it works on all kinds of audio
sources.
## Solution
- Added public `start_position` and `duration` fields to
`PlaybackSettings`, both of type `Option<std::time::Duration>`.
- Used rodio's `Source::skip_duration` and `Source::take_duration`
functions to implement start position and duration, respectively.
- If the audio is looping, it interacts as you might expect - the loop
will start at the start position and end after the duration.
- If the start position is None (the default value), the audio will
start from the beginning, like normal. Similarly, if the duration is
None (default), the audio source will play for as long as possible.
## Testing
I tried adding a custom start position to all the existing audio
examples to test a bunch of different audio sources and settings, and
they all worked fine. I verified that it skips the right amount of time,
and that it skips the entire audio clip if the start position is longer
than the length of the clip. All my testing was done on Fedora Linux.
Update: I did similar testing for duration, and ensured that the two
options worked together in combination and interacted well with looping
audio.
---
## Showcase
```rust
// Play a 10 second segment of a song, starting at 0:30.5
commands.spawn((
AudioPlayer::new(song_handle),
PlaybackSettings::LOOP
.with_start_position(Duration::from_secs_f32(30.5))
.with_duration(Duration::from_secs(10))
));
```
# Objective
Recently the `u32` `Entity::generation` was replaced with the new
`EntityGeneration` in #19121.
This made meanings a lot more clear, and prevented accidental misuse.
One common misuse was assuming that `u32`s that were greater than others
came after those others.
Wrapping makes this assumption false.
When `EntityGeneration` was created, it retained the `u32` ordering,
which was useless at best and wrong at worst.
This pr fixes the ordering implementation, so new generations are
greater than older generations.
Some users were already accounting for this ordering issue (which was
still present in 0.16 and before) by manually accessing the `u32`
representation. This made migrating difficult for avian physics; see
[here](https://discord.com/channels/691052431525675048/749335865876021248/1377431569228103780).
I am generally of the opinion that this type should be kept opaque to
prevent accidental misuse.
As we find issues like this, the functionality should be added to
`EntityGeneration` directly.
## Solution
Fix the ordering implementation through `Ord`.
Alternatively, we could keep `Ord` the same and make a `cmp_age` method,
but I think this is better, even though sorting entity ids may be
*marginally* slower now (but more correct). This is a tradeoff.
## Testing
I improved documentation for aliasing and ordering, adding some doc
tests.
# Objective
There are several uninlined format args (seems to be in more formatting
macros and in more crates) that are not detected on stable, but are on
nightly.
## Solution
Fix them.
# Objective
#19047 added an `MaybeUninit` field to `EntityMeta`, but did not
guarantee that it will be initialized before access:
```rust
let mut world = World::new();
let id = world.entities().reserve_entity();
world.flush();
world.entity(id);
```
<details>
<summary>Miri Error</summary>
```
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
--> /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1121:26
|
1121 | unsafe { meta.spawned_or_despawned.assume_init() }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside closure at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1121:26: 1121:65
= note: inside `std::option::Option::<&bevy_ecs::entity::EntityMeta>::map::<bevy_ecs::entity::SpawnedOrDespawned, {closure@bevy_ecs::entity::Entities::entity_get_spawned_or_despawned::{closure#1}}>` at /home/vj/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:1144:29: 1144:33
= note: inside `bevy_ecs::entity::Entities::entity_get_spawned_or_despawned` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1112:9: 1122:15
= note: inside closure at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1094:13: 1094:57
= note: inside `bevy_ecs::change_detection::MaybeLocation::<std::option::Option<&std::panic::Location<'_>>>::new_with_flattened::<{closure@bevy_ecs::entity::Entities::entity_get_spawned_or_despawned_by::{closure#0}}>` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/change_detection.rs:1371:20: 1371:24
= note: inside `bevy_ecs::entity::Entities::entity_get_spawned_or_despawned_by` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1093:9: 1096:11
= note: inside `bevy_ecs::entity::Entities::entity_does_not_exist_error_details` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1163:23: 1163:70
= note: inside `bevy_ecs::entity::EntityDoesNotExistError::new` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1182:22: 1182:74
= note: inside `bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell::<'_>::get_entity` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/unsafe_world_cell.rs:368:20: 368:73
= note: inside `<bevy_ecs::entity::Entity as bevy_ecs::world::WorldEntityFetch>::fetch_ref` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/entity_fetch.rs:207:21: 207:42
= note: inside `bevy_ecs::world::World::get_entity::<bevy_ecs::entity::Entity>` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/mod.rs:911:18: 911:42
note: inside `main`
--> src/main.rs:12:15
|
12 | world.entity(id);
|
```
</details>
## Solution
- remove the existing `MaybeUninit` in `EntityMeta.spawned_or_despawned`
- initialize during flush. This is not needed for soundness, but not
doing this means we can't return a sensible location/tick for flushed
entities.
## Testing
Test via the snippet above (also added equivalent test).
---------
Co-authored-by: urben1680 <55257931+urben1680@users.noreply.github.com>
# Objective
- Related to #19024
## Solution
- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_pbr` (excluding meshlets).
## Testing
- `atmosphere` example still works
- `fog` example still works
- `decal` example still works
P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
# Objective
- Related to #19024
## Solution
- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_ui`.
## Testing
- `box_shadow` example still works.
- `gradient` example is broken at head (see #19384) - but otherwise
gives the same result in the console.
- `ui_materials` example still works.
- `ui_texture_slice` example still works.
P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
# Objective
- Related to #19024
## Solution
- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_gizmos`.
## Testing
- `2d_gizmos` example still works.
- `3d_gizmos` example still works.
P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
# Objective
- Related to #19024
## Solution
- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_core_pipeline`.
## Testing
- `bloom_3d` example still works.
- `motion_blur` example still works.
- `meshlet` example still works (it uses a shader from core).
P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
# Objective
Renames `Timer::finished` and `Timer::paused` to `Timer::is_finished`
and `Timer::is_paused` to align the public APIs for `Time`, `Timer`, and
`Stopwatch`.
Fixes#19110
# Objective
Fixes#18905
## Solution
`world.commands().entity(target_entity).queue(command)` calls
`commands.with_entity` without an error handler, instead queue on
`Commands` with an error handler
## Testing
Added unit test
Co-authored-by: Heart <>
# Objective
Remove `ArchetypeComponentId` and `archetype_component_access`.
Following #16885, they are no longer used by the engine, so we can stop
spending time calculating them or space storing them.
## Solution
Remove `ArchetypeComponentId` and everything that touches it.
The `System::update_archetype_component_access` method no longer needs
to update `archetype_component_access`. We do still need to update query
caches, but we no longer need to do so *before* running the system. We'd
have to touch every caller anyway if we gave the method a better name,
so just remove `System::update_archetype_component_access` and
`SystemParam::new_archetype` entirely, and update the query cache in
`Query::get_param`.
The `Single` and `Populated` params also need their query caches updated
in `SystemParam::validate_param`, so change `validate_param` to take
`&mut Self::State` instead of `&Self::State`.
# Objective
- move SyncCell and SyncUnsafeCell to bevy_platform
## Solution
- move SyncCell and SyncUnsafeCell to bevy_platform
## Testing
- cargo clippy works
# Objective
- Related to #19024
## Solution
- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_sprite`.
## Testing
- `sprite` example still works.
- `mesh2d` example still works.
P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
# Objective
- A step towards splitting out bevy_camera from bevy_render
## Solution
- Move a shim type into bevy_utils to avoid a dependency cycle
- Manually expand Deref/DerefMut to avoid having a bevy_derive
dependency so early in the dep tree
## Testing
- It compiles
Fixes#19081.
Simply created a duplicate of the existing `insert_if_new` test, but
using sparse sets.
## Testing:
The test passes on main, but fails if #19059 is reverted.
# Objective
- Related to #19024
## Solution
- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_anti_aliasing`.
## Testing
- `anti_aliasing` example still works.
P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
# Objective
Found a typo while looking at gradients in another issue and gave the
docs a skim for more.
## Solution
A couple typo fixes and some tiny improvements
# Objective
Constify `Val::resolve` and `BorderRadius::resolve`
# Solution
* Replace uses of `Vec2::min_element` and `Vec2::max_element` with `min`
and `max` called on the components.
* Make `BorderRadius::resolve` and `BorderRadius::resolve_single_corner`
`const`.
* Swap the order of the `bottom_left` and `bottom_right` fields of
`BorderRadius` and `ResolvedBorderRadius` so they match the ccw order
used in the shader and in css.
# Objective
- Enable state scoped entities by default
- Provide a way to disable it when needed
---------
Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
# Objective
- Related to #19024
## Solution
- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
bevy_render.
## Testing
- `animate_shader` example still works
P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
# Objective
Allow creating a new window without it being focused, when `Window`'s
`focused` is `false.
## Solution
Use `winit`'s `WindowBuilder`'s `with_active` method
## Notes
- `winit`'s doc lists [redox's
`Orbital`](https://gitlab.redox-os.org/redox-os/orbital) as an
unsupported platform, but since Bevy doesn't officially support this
platform, I didn't put it in the documentation.
- I only tested on Linux, which is an unsupported platform. I can give
you a test code if you want to test on another platform.
- I initially put a line
[here](https://github.com/bevyengine/bevy/blob/v0.11.0/crates/bevy_winit/src/system.rs#L72)
to set the Bevy `Window`'s `focused` to `winit_window.has_focus()` after
window creation to avoid the case where `with_active` is not supported,
the window is spawned focused, no `WindowFocused` event is triggered,
and Bevy `Window` would be desynced from winit's window. But after
testing on Linux (which doesn't support `with_active`) it seems like at
that point `has_focus` returns `false` and the event is triggered, so I
removed it. Do you think I should add it back to be safe?
## Changelog
- A new unfocused `Window` can be created by setting `focused` to
`false`.
## Migration Guide
- If a `Window` is spawned with `focused` set to `false`, it will now
start not focused on supported platforms.
Adopted from #9208
---------
Co-authored-by: Sélène Amanita <selene.amanita@net-c.com>
Co-authored-by: Sélène Amanita <134181069+Selene-Amanita@users.noreply.github.com>
Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Fix some grammatical errors: it's -> its
Not the most useful commit in the world, but I saw a couple of these and
decided to fix the lot.
## Solution
-
## Testing
-
# Objective
Fix https://github.com/bevyengine/bevy/issues/13390
## Solution
The second parameter of the remove_reflect function is called
component_type_name in ReflectCommandExt but component_type_path in the
implementation for EntityCommands. Use component_type_path in both
places.
## Testing
None
# Objective
cargo update was required to build because into_values was added in a
patch version
## Solution
Depend on the new patch
## Testing
Builds locally now
# Objective
Fixes#17983
## Solution
Implemented a basic `Dir4` struct with methods similar to `Dir3` and
`Dir2`.
## Testing
- Did you test these changes? If so, how?
Added unit tests that follow the same pattern of the other Dir structs.
- Are there any parts that need more testing?
Since the other Dir structs use rotations to test renormalization and I
have been following them as a pattern for the Dir4 struct I haven't
implemented a test to cover renormalization of Dir4's.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
Use Dir4 in the wild.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
N/A (Tested on Linux/X11 but it shouldn't be a problem)
# Objective
Fixes#19156
## Solution
There was a call to sort by `TabIndex` after iterating through all the
`TabGroup`s. Simply removing that line of code made the new test case
pass (navigating through `TabIndex` within different `TabGroup`s and
kept everything else working as expected as far as I can tell.
I went ahead and broke the `focusable` vec down into per-group vecs that
get sorted by `TabIndex` and appended to the main vec in group sorted
order to maintain the sorting that was being done before, but respecting
the `TabGroup` sorting that was missing in the issue that this PR
addresses.
## Testing
I added a new unit test that reproduced the issue outlined above that
will run in CI. This test was failing before deleting the sort, and now
both unit tests are passing.
# Objective
Fixes#18790.
Simpler alternative to #19195.
## Solution
As suggested by @PixelDust22, simply avoid overwriting the pass if the
schedule already has auto sync points enabled.
Leave pass logic untouched.
It still is probably a bad idea to add systems/set configs before
changing the build settings, but that is not important as long there are
no more complex build passes.
## Testing
Added a test.
---------
Co-authored-by: Thierry Berger <contact@thierryberger.com>
# Objective
- The WakeUp event is never added to the app. If you need to use that
event you currently need to add it yourself.
## Solution
- Add the WakeUp event to the App in the WinitPlugin
## Testing
- I tested the window_setting example and it compiled and worked
# Objective
- Simplify `Camera` initialization
- allow effects to require HDR
## Solution
- Split out `Camera.hdr` into a marker `Hdr` component
## Testing
- ran `bloom_3d` example
---
## Showcase
```rs
// before
commands.spawn((
Camera3d
Camera {
hdr: true
..Default::default()
}
))
// after
commands.spawn((Camera3d, Hdr));
// other rendering components can require that the camera enables hdr!
// currently implemented for Bloom, AutoExposure, and Atmosphere.
#[require(Hdr)]
pub struct Bloom;
```
> [!important]
> To **maintainers**: we should wait to merge this one in until after
#18944 lands so that cherry-picking the latter for 0.16.1 is simpler.
# Objective
The `std` module is where we implement the reflection traits for types
that are exported from `std` (including `core` and `alloc`). Over time,
this file has grown increasingly large, making it difficult to navigate
and a pain point for merge conflicts.
The goal of this PR is to break up the module into smaller chunks.
## Solution
The `std` module has been split into many submodules:
- `alloc`
- `bevy_platform`
- `core`
- `std`
Each of these new modules is comprised of submodules that closely
resemble the actual module. For example, the impls for
`::alloc::vec::Vec` have been moved to
`bevy_reflect::impls::alloc::vec::Vec`.
Some liberties were taken. For example, `Cow<'static, Path>` was kept in
`bevy_reflect::impls::std::path` rather than
`bevy_reflect::impls::alloc::borrow`.
You may ask: _Isn't this a little overkill? Why does the one-line impl
for `TypeId` need its own file?_
And yes, it is partly overkill. But the benefit with this approach is
that where an `std`-related type should live is mostly unambiguous. If
we wanted to reflect `::core::net::Ipv4Addr`, it's very clear that it
should be done in `bevy_reflect::impls::core::net`.
We can discuss better ways of breaking this up if people have other
ideas or opinions, but I think this is a pretty straightforward way of
doing it.
### Note to Reviewers
The code is pretty much copy-paste from the mega module to the new
submodules. It's probably best to focus efforts on reviewing the general
module structure, as well as maybe which impls are included where.
You _can_ review the code contained within each impl, but I promise you
the only thing I touched were the paths in the macros so they could be
more hygienic :)
## Testing
You can just check that everything compiles still:
```
cargo check -p bevy_reflect --tests
```
# Objective
- The tigger_screenshots system gets added in `.build()` but relies on a
resource that is only inserted in `.finish()`
- This isn't a bug for most users, but when doing headless mode testing
it can technically work without ever calling `.finish()` and did work
before bevy 0.15 but while migrating my work codebase I had an issue of
test failing because of this
## Solution
- Move the trigger_screenshots system to `.finish()`
## Testing
- I ran the screenshot example and it worked as expected
# Objective
- Allow compressed image formats to be used with `ImagePlugin` and
`GltfPlugin` in cases where there is no `RenderDevice` resource. (For
example, when using a custom render backend)
## Solution
- Define a `CompressedImageFormatSupport` component that allows the user
to explicitly determine which formats are supported.
~~Not sure if this is the best solution. Alternatively, I considered
initializing CompressedImageFormatSupport from render device features
separately, it would need to run after the render device is initialized
but before `ImagePlugin` and `GltfPlugin` finish. Not sure where the
best place for that to happen would be.~~
Update: decided on going with @greeble-dev solution: defining the
`CompressedImageFormatSupport` resource in `bevy_image`, but letting
`bevy_render` register the resource value.
## produce a DragEnter event when reentering the dragged entity
when making a piano, i want dragging across the keys to trigger the
notes of each key, but currently if i drag out of a key, then back to
it, this will not work since the dragged entity gets filtered out
## Solution
- make DragEnter event work whenever there's an entry. if the user wants
to ignore the dragged entity they can compare `target` and `dragged`
## Testing
- tested this with a modified version of the 2d_shapes example. i added
an observer to the entities: (and added mesh picking plugin)
```rust
.observe(|t: Trigger<Pointer<DragEnter>>| {
info!("entered {}, started from {}", t.target(), t.dragged);
}
```
- i'm not sure if other things need more testing, or if this is wrong
completely and breaks other things i don't know of!
---
## Showcase
before:
https://github.com/user-attachments/assets/48de606a-e44d-4ca1-ae16-d8dcef640d6e
after:
https://github.com/user-attachments/assets/b1be231f-c826-47bc-be43-c637f22e7846
# Objective
- Allow users to get the playback position of playing audio.
## Solution
- Add a `position` method to `AudioSinkPlayback`
- Implement it for `AudioSink` and `SpatialAudioSink`
## Testing
- Updated `audio_control` example to show playback position
# Objective
- Update AccessKit crates to their latest versions.
- Fixes#19040
## Solution
- Only modifying Cargo.toml files is needed, few changes under the hood
but nothing impacting Bevy.
## Testing
- I ran the tab_navigation example on Windows 11.
# Objective
Since #18704 is done, we can track the length of unique entity row
collections with only a `u32` and identify an index within that
collection with only a `NonMaxU32`. This leaves an opportunity for
performance improvements.
## Solution
- Use `EntityRow` in sparse sets.
- Change table, entity, and query lengths to be `u32` instead of
`usize`.
- Keep `batching` module `usize` based since that is reused for events,
which may exceed `u32::MAX`.
- Change according `Range<usize>` to `Range<u32>`. This is more
efficient and helps justify safety.
- Change `ArchetypeRow` and `TableRow` to wrap `NonMaxU32` instead of
`u32`.
Justifying `NonMaxU32::new_unchecked` everywhere is predicated on this
safety comment in `Entities::set`: "`location` must be valid for the
entity at `index` or immediately made valid afterwards before handing
control to unknown code." This ensures no entity is in two table rows
for example. That fact is used to argue uniqueness of the entity rows in
each table, archetype, sparse set, query, etc. So if there's no
duplicates, and a maximum total entities of `u32::MAX` none of the
corresponding row ids / indexes can exceed `NonMaxU32`.
## Testing
CI
---------
Co-authored-by: Christian Hughes <9044780+ItsDoot@users.noreply.github.com>
# Objective
- Make errors in #19124#13289 clearer, and opt for option 1. of
https://github.com/gfx-rs/wgpu/issues/7677
## Solution
Remove the round to block size `.physical_size(texture_format);`
Error message now becomes much clearer:
```
thread 'Compute Task Pool (5)' panicked at E:\r\wgpu\wgpu\src\backend\wgpu_core.rs:1423:26:
wgpu error: Validation Error
Caused by:
In Device::create_texture
Width 2050 is not a multiple of Bc7RgbaUnormSrgb's block width (4)
```
## Testing
- Tested using the repro in #19124
# Objective
- Fix `AssetChanged` code documentation to mention the `PostUpdate`
schedule instead of the `Last` schedule
## Testing
- Trivial (code doc). Check `bevy_asset/src/lib.rs` in function
`init_asset` to see where this is scheduled:
```rust
.add_systems(
PostUpdate,
Assets::<A>::asset_events
.run_if(Assets::<A>::asset_events_condition)
.in_set(AssetEvents),
)
```
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
`Image::resize` currently prints a warning when resizing an
uninitialized `Image`, even though the resizing works correctly. For
[code](c92f14c9e7/crates/bevy_ui/src/widget/viewport.rs (L175-L179))
that doesn't care about whether the image is initialized CP-side, this
is inconvenient and unnecessary.
# Objective
Ran into a situation where I need to compare two image samplers. My
current workaround is to compare the `Debug` outputs
## Solution
Derive `PartialEq` on `ImageSampler` and structs in its fields.
## Testing
Full CI passed.
# Objective
The new viewport example allocates a texture in main memory, even though
it's only needed on the GPU. Also fix an unnecessary warning when a
viewport's texture doesn't exist CPU-side.
## Testing
Run the `viewport_node` example.
# Objective
allow specifying the left/top/right/bottom border colors separately for
ui elements
fixes#14773
## Solution
- change `BorderColor` to
```rs
pub struct BorderColor {
pub left: Color,
pub top: Color,
pub right: Color,
pub bottom: Color,
}
```
- generate one ui node per distinct border color, set flags for the
active borders
- render only the active borders
i chose to do this rather than adding multiple colors to the
ExtractedUiNode in order to minimize the impact for the common case
where all border colors are the same.
## Testing
modified the `borders` example to use separate colors:

the behaviour is a bit weird but it mirrors html/css border behaviour.
---
## Migration:
To keep the existing behaviour, just change `BorderColor(color)` into
`BorderColor::all(color)`.
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
# Objective
- Currently, the error span for `get_struct_field` when encountering an
enum or union points to the macro invocation, rather than the `enum` or
`union` token. It also doesn't mention which macro reported the error.
## Solution
- Report the correct error span
- Add parameter for passing in the name of the macro invocation
## Testing
Bevy compiles fine with this change
## Migration Guide
```rs
// before
let fields = get_struct_fields(&ast.data);
// after
let fields = get_struct_fields(&ast.data, "derive(Bundle)");
```
---------
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Hiya!
# Objective
- Remove upcasting methods that are no longer necessary since Rust 1.86.
- Cleanup the interned label code.
## Notes
- I didn't try to remove the upcasting methods from `bevy_reflect`, as
there appears to be some complexity related to remote type reflection.
- There are likely some other upcasting methods floating around.
## Testing
I ran the `breakout` example to check that the hashing/eq
implementations of the labels are still correct.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
similar to https://github.com/bevyengine/bevy/pull/12030
# Objective
`bevy_mod_debugdump` uses the `SystemTypeSet::system_type` to look up
constrains like `(system_1, system_2.after(system_1))`. For that it
needs to find the type id in `schedule.graph().systems()`
Now with systems being wrapped in an `InfallibleSystemWrapper` this
association was no longer possible.
## Solution
By forwarding the type id in `InfallibleSystemWrapper`,
`bevy_mod_debugdump` can resolve the dependencies as before, and the
wrapper is an unnoticable implementation detail.
## Testing
- `cargo test -p bevy_ecs`
I'm not sure what exactly could break otherwise.
# Objective
- Fixes#18869.
## Solution
The issue was the `?` after a `Result` raising the error, instead of
treating it.
Instead it is handled with `ok`, `and_then`, `map` ...
_Edit: I added the following logic._
On `bevy/query` remote requests, when `strict` is false:
- Unregistered components in `option` and `without` are ignored.
- Unregistered components in `has` are considered absent from the
entity.
- Unregistered components in `components` and `with` result in an empty
response since they specify hard requirements.
I made the `get_component_ids` function return a
`AnyhowResult<(Vec<(TypeId, ComponentId)>, Vec<String>)>` instead of the
previous `AnyhowResult<Vec<(TypeId, ComponentId)>>`; that is I added the
list of unregistered components.
## Testing
I tested changes using the same procedure as in the linked issue:
```sh
cargo run --example server --features="bevy_remote"
```
In another terminal:
```sh
# Not strict:
$ curl -X POST http://localhost:15702 -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "method": "bevy/query", "id": 0, "params": { "data": { "components": [ "foo::bar::MyComponent" ] } } }'
{"jsonrpc":"2.0","id":0,"result":[]}
# Strict:
$ curl -X POST http://localhost:15702 -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "method": "bevy/query", "id": 0, "params": { "data": { "components": [ "foo::bar::MyComponent" ] }, "strict": true } }'
{"jsonrpc":"2.0","id":0,"error":{"code":-23402,"message":"Component `foo::bar::MyComponent` isn't registered or used in the world"}}
```
# Objective
It's not possible atm for third-party crates to create their own text
systems that use the existing cosmic-text `FontSystem` and font atlases.
## Solution
Make `FontFaceInfo`, `TextPipeline::map_handle_to_font_id`, and
`load_font_fontdb` public so third-party crates can access and update
the existing font atlases
## Objective
Fix#19114.
## Solution
#17875 changed the glTF importer to make sure that sampler filters are
linear when anisotropic filtering is enabled - this is required by
`wgpu`. But the condition was mistakenly inverted, so it forces the
filtering to linear when anisotropic filtering is _not_ enabled.
## Testing
```
cargo run --example color_grading
cargo run --example testbed_3d
```
# Objective
Now that `bevy_platform::cfg` is merged, we can start tidying up
features. This PR starts with `bevy_utils`.
## Solution
- Removed `serde` and `critical-section` features (they were just
re-exports of `bevy_platform` anyway)
- Removed `std`, `alloc` features, relying on `bevy_platform::cfg` to
check for availability.
- Added `parallel` feature to provide access to the `Parallel` type.
- Moved the `HashMap` type aliases into `map.rs` for better
organisation.
## Testing
- CI
Stores mesh names from glTF files in GltfMeshName component rather than
Name component, making both GltfMeshName and GltfMaterialName behave
like strings via Deref.
# Objective
Fixed the side effects of #19287
Fixes Examples that modify gltf materials are broken #19322
## Solution
Add GltfMeshName component and Deref implementations
Stores mesh names from glTF files in GltfMeshName component rather than
Name component, making both GltfMeshName and GltfMaterialName behave
like strings via Deref.
## Testing
cargo run --example depth_of_field
cargo run --example lightmaps
cargo run --example mixed_lighting
They are consistent with the situation before the error occurred.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Rob Parrett <robparrett@gmail.com>
# Objective
Closes#19175
Make `LogDiagnosticsState` public to be able to edit its filters
## Solution
Make `LogDiagnosticsState` public and add methods to allow editing the
duration and filter
## Testing
`cargo run -p ci`
## Showcase
Updated `log_diagnostics` example

# Objective
Fix incorrect average returned by `Diagnostic` after `clear_history` is
called.
## Solution
Reset sum and ema values in `Diagnostic::clear_history`.
## Testing
I have added a cargo test for `Diagnostic::clear_history` that checks
average and smoothed average. This test passes, and should not be
platform dependent.
# Objective
Remove errant "a" from docs.
(I'm assuming that this sort of trivial fix is easy enough to merge that
it's worth doing, but let me know if you'd prefer me to not bother.)
# Objective
allow serialization / deserialization on the `ChildOf` entity, for
example in network usage.
my usage was for the bevy_replicon crate, to replicate `ChildOf`.
## Solution
same implementation of serde as other types in the bevy repo
---------
Co-authored-by: Hennadii Chernyshchyk <genaloner@gmail.com>
## Objective
Add documentation useful to users of `bevy_ecs` not also using `App`.
Fixes#19270.
## Solution
* Add explanation of labels to `Schedule` documentation.
* Add example of `derive(ScheduleLabel)` to `trait ScheduleLabel`.
* Add a third example to `Schedule` which demonstrates using a schedule
via label instead of owning it directly.
* Add further explanation and links to `World::add_schedule()`, and
`World::run_schedule()`.
## Testing
Reviewed generated documentation.
Please review this documentation carefully for correctness, as I have
little experience with `bevy_ecs` and I am adding this information
because it would have helped my own past confusion, but I may still be
wrong about how things should be done.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
# Objective
Fixes#19120
## Solution
Use the find and replace token feature in VSCode to replace all the
`Condition`s with `SystemCondition`s. Then look through all the
documentation with find and replace to replace all the `Condition`s
there.
## Testing
- Did you test these changes? If so, how?
Yes, used cargo clippy, cargo build and cargo test.
- Are there any parts that need more testing?
Nope
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
By compiling and running bevy
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
Shouldn't be, but Fedora Linux with KDE Wayland
## Objective
Fix the misleading 2d anchor API where `Anchor` is a component and
required by `Text2d` but is stored on a field for sprites.
Fixes#18367
## Solution
Remove the `anchor` field from `Sprite` and require `Anchor` instead.
## Migration Guide
The `anchor` field has been removed from `Sprite`. Instead the `Anchor`
component is now a required component on `Sprite`.
# Objective
Allowing drawing of UI nodes with a gradient instead of a flat color.
## Solution
The are three gradient structs corresponding to the three types of
gradients supported: `LinearGradient`, `ConicGradient` and
`RadialGradient`. These are then wrapped in a `Gradient` enum
discriminator which has `Linear`, `Conic` and `Radial` variants.
Each gradient type consists of the geometric properties for that
gradient and a list of color stops.
Color stops consist of a color, a position or angle and an optional
hint. If no position is specified for a stop, it's evenly spaced between
the previous and following stops. Color stop positions are absolute, if
you specify a list of stops:
```vec


Conic gradients can be used to draw simple pie charts like in CSS:

# Objective
Spot light shadows are still broken after fixing point lights in #19265
## Solution
Fix spot lights in the same way, just using the spot light specific
visible entities component. I also changed the query to be directly in
the render world instead of being extracted to be more accurate.
## Testing
Tested with the same code but changing `PointLight` to `SpotLight`.
# Objective
Add documentation for the last two functions in bevy_picking that are
missing them.
## Solution
Add boilerplate "Constructs an X" to `PointerHits::new()` and
`HitData::new()`.
This form of no-information documentation of `new()` functions is used
in several places in the repo, and @alice-i-cecile agreed that this is a
reasonable approach - the params are already documented on the fields
within the struct definition.
---------
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
# Objective
Been looking for simplifications in the text systems as part of the text
input changes.
This enum isn't very helpful I think. We can remove it and the
associated parameters and instead just negate the glyph's y-offsets in
`extract_text2d_sprite`.
## Solution
Remove the `YAxisOrientation` enum and parameters.
Queue text sprites relative to the top-left in `extract_text2d_sprite`
and negate the glyph's y-offset.
## Testing
The `text2d` example can be used for testing:
```
cargo run --example text2d
```
# Objective
[see original
comment](https://github.com/bevyengine/bevy/pull/18801#issuecomment-2796981745)
> Alternately, could we store it on the World instead of a global? I
think we have a World nearby whenever we call default_error_handler().
That would avoid the need for atomics or locks, since we could do
ordinary reads and writes to the World.
Global error handlers don't actually need to be global – per world is
enough. This allows using different handlers for different worlds and
also removes the restrictions on changing the handler only once.
## Solution
Each `World` can now store its own error handler in a resource.
For convenience, you can also set the default error handler for an
`App`, which applies it to the worlds of all `SubApp`s. The old behavior
of only being able to set the error handler once is kept for apps.
We also don't need the `configurable_error_handler` feature anymore now.
## Testing
New/adjusted tests for failing schedule systems & observers.
---
## Showcase
```rust
App::new()
.set_error_handler(info)
…
```
# Objective
Fixes#19150
## Solution
Normally the `validate_cached_entity` in
86cc02dca2/crates/bevy_pbr/src/prepass/mod.rs (L1109-L1126)
marks unchanged entites as clean, which makes them remain in the phase.
If a material is changed to an `alpha_mode` that isn't supposed to be
added to the prepass pipeline, the specialization system just
`continue`s and doesn't indicate to the cache that the entity is not
clean anymore.
I made these invalid entities get removed from the pipeline cache so
that they are correctly not marked clean and then removed from the
phase.
## Testing
Tested with the example code from the issue.
# Objective
Fixes#18945
## Solution
Entities that are not visible in any view (camera or light), get their
render meshes removed. When they become visible somewhere again, the
meshes get recreated and assigned possibly different ids.
Point/spot light visible entities weren't cleared when the lights
themseves went out of view, which caused them to try to queue these fake
visible entities for rendering every frame. The shadow phase cache
usually flushes non visible entites, but because of this bug it never
flushed them and continued to queue meshes with outdated ids.
The simple solution is to every frame clear all visible entities for all
point/spot lights that may or may not be visible. The visible entities
get repopulated directly afterwards. I also renamed the
`global_point_lights` to `global_visible_clusterable` to make it clear
that it includes only visible things.
## Testing
- Tested with the code from the issue.
# Objective
- transitive shader imports sometimes fail to load silently and return
Ok
- Fixes#19226
## Solution
- Don't return Ok, return the appropriate error code which will retry
the load later when the dependencies load
## Testing
- `bevy run --example=3d_scene web --open`
Note: this is was theoretically a problem before the hot reloading PR,
but probably extremely unlikely to occur.
# Objective
- Get in-engine shader hot reloading working
## Solution
- Adopt #12009
- Cut back on everything possible to land an MVP: we only hot-reload PBR
in deferred shading mode. This is to minimize the diff and avoid merge
hell. The rest shall come in followups.
## Testing
- `cargo run --example pbr --features="embedded_watcher"` and edit some
pbr shader code
`bevy_ecs` was meant to have the `States` and `SubStates`
`proc_macro_derive`s removed when the separate `bevy_state` [was
created](https://github.com/bevyengine/bevy/issues/13216) but they were
missed.
# Objective
Fixes#19027
## Solution
Query for the material binding id if using fallback CPU processing
## Testing
I've honestly no clue how to test for this, and I imagine that this
isn't entirely failsafe :( but would highly appreciate a suggestion!
To verify this works, please run the the texture.rs example using WebGL
2.
Additionally, I'm extremely naive about the nuances of pbr. This PR is
essentially to kinda *get the ball rolling* of sorts. Thanks :)
---------
Co-authored-by: Gilles Henaux <ghx_github_priv@fastmail.com>
Co-authored-by: charlotte <charlotte.c.mcelwain@gmail.com>
# Objective
- Fix#14246
## Solution
- If building for wasm windows, add a bit of code that replaces `\\`
with `/` in the `file!()` arg
## Testing
- Used MRE https://github.com/janhohenheim/asset-crash
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Fixes#19130
## Solution
Fully quality `Result::Ok` so as to not accidentally invoke the anyhow
function of the same name
## Testing
Tested on this minimal repro with and without change.
main.rs
```rs
use anyhow::Ok;
use bevy::ecs::system::SystemParam;
#[derive(SystemParam)]
pub struct SomeParams;
fn main() {
}
```
Cargo.toml
```toml
[package]
name = "bevy-playground"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.98"
bevy = { path = "../bevy" }
```
# Objective
resolves#19092
## Solution
- remove the `.saturating_sub` from the index transformation
- add `.saturating_add` to the internal offset calculation
## Testing
- added regression test, confirming 0 index order + testing max bound
# Objective
In my own project I was encountering the issue to find out which
entities were spawned after applying commands. I began maintaining a
vector of all entities with generational information before and after
applying the command and diffing it. This was awfully complicated though
and has no constant complexity but grows with the number of entities.
## Solution
Looking at `EntyMeta` it seemed obvious to me that struct can track the
tick just as it does with `MaybeLocation`, updated from the same call.
After that it became almost a given to also introduce query data
`SpawnDetails` which offers methods to get the spawn tick and location,
and query filter `Spawned` that filters entities out that were not
spawned since the last run.
## Testing
I expanded a few tests and added new ones, though maybe I forgot a group
of tests that should be extended too. I basically searched `bevy_ecs`
for mentions of `Changed` and `Added` to see where the tests and docs
are.
Benchmarks of spawn/despawn can be found
[here](https://github.com/bevyengine/bevy/pull/19047#issuecomment-2852181374).
---
## Showcase
From the added docs, systems with equal complexity since the filter is
not archetypal:
```rs
fn system1(q: Query<Entity, Spawned>) {
for entity in &q { /* entity spawned */ }
}
fn system2(query: Query<(Entity, SpawnDetails)>) {
for (entity, spawned) in &query {
if spawned.is_spawned() { /* entity spawned */ }
}
}
```
`SpawnedDetails` has a few more methods:
```rs
fn print_spawn_details(query: Query<(Entity, SpawnDetails)>) {
for (entity, spawn_details) in &query {
if spawn_details.is_spawned() {
print!("new ");
}
println!(
"entity {:?} spawned at {:?} by {:?}",
entity,
spawn_details.spawned_at(),
spawn_details.spawned_by()
);
}
}
```
## Changes
No public api was changed, I only added to it. That is why I added no
migration guide.
- query data `SpawnDetails`
- query filter `Spawned`
- method `Entities::entity_get_spawned_or_despawned_at`
- method `EntityRef::spawned_at`
- method `EntityMut::spawned_at`
- method `EntityWorldMut::spawned_at`
- method `UnsafeEntityCell::spawned_at`
- method `FilteredEntityRef::spawned_at`
- method `FilteredEntityMut::spawned_at`
- method `EntityRefExcept::spawned_at`
- method `EntityMutExcept::spawned_at`
---------
Co-authored-by: Eagster <79881080+ElliottjPierce@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
This is a followup to #18704 . There's lots more followup work, but this
is the minimum to unblock #18670, etc.
This direction has been given the green light by Alice
[here](https://github.com/bevyengine/bevy/pull/18704#issuecomment-2853368129).
## Solution
I could have split this over multiple PRs, but I figured skipping
straight here would be easiest for everyone and would unblock things the
quickest.
This removes the now no longer needed `identifier` module and makes
`Entity::generation` go from `NonZeroU32` to `struct
EntityGeneration(u32)`.
## Testing
CI
---------
Co-authored-by: Mark Nokalt <marknokalt@live.com>
# Objective
There are two problems this aims to solve.
First, `Entity::index` is currently a `u32`. That means there are
`u32::MAX + 1` possible entities. Not only is that awkward, but it also
make `Entity` allocation more difficult. I discovered this while working
on remote entity reservation, but even on main, `Entities` doesn't
handle the `u32::MAX + 1` entity very well. It can not be batch reserved
because that iterator uses exclusive ranges, which has a maximum upper
bound of `u32::MAX - 1`. In other words, having `u32::MAX` as a valid
index can be thought of as a bug right now. We either need to make that
invalid (this PR), which makes Entity allocation cleaner and makes
remote reservation easier (because the length only needs to be u32
instead of u64, which, in atomics is a big deal), or we need to take
another pass at `Entities` to make it handle the `u32::MAX` index
properly.
Second, `TableRow`, `ArchetypeRow` and `EntityIndex` (a type alias for
u32) all have `u32` as the underlying type. That means using these as
the index type in a `SparseSet` uses 64 bits for the sparse list because
it stores `Option<IndexType>`. By using `NonMaxU32` here, we cut the
memory of that list in half. To my knowledge, `EntityIndex` is the only
thing that would really benefit from this niche. `TableRow` and
`ArchetypeRow` I think are not stored in an `Option` in bulk. But if
they ever are, this would help. Additionally this ensures
`TableRow::INVALID` and `ArchetypeRow::INVALID` never conflict with an
actual row, which in a nice bonus.
As a related note, if we do components as entities where `ComponentId`
becomes `Entity`, the the `SparseSet<ComponentId>` will see a similar
memory improvement too.
## Solution
Create a new type `EntityRow` that wraps `NonMaxU32`, similar to
`TableRow` and `ArchetypeRow`.
Change `Entity::index` to this type.
## Downsides
`NonMax` is implemented as a `NonZero` with a binary inversion. That
means accessing and storing the value takes one more instruction. I
don't think that's a big deal, but it's worth mentioning.
As a consequence, `to_bits` uses `transmute` to skip the inversion which
keeps it a nop. But that also means that ordering has now flipped. In
other words, higher indices are considered less than lower indices. I
don't think that's a problem, but it's also worth mentioning.
## Alternatives
We could keep the index as a u32 type and just document that `u32::MAX`
is invalid, modifying `Entities` to ensure it never gets handed out.
(But that's not enforced by the type system.) We could still take
advantage of the niche here in `ComponentSparseSet`. We'd just need some
unsafe manual conversions, which is probably fine, but opens up the
possibility for correctness problems later.
We could change `Entities` to fully support the `u32::MAX` index. (But
that makes `Entities` more complex and potentially slightly slower.)
## Testing
- CI
- A few tests were changed because they depend on different ordering and
`to_bits` values.
## Future Work
- It might be worth removing the niche on `Entity::generation` since
there is now a different niche.
- We could move `Entity::generation` into it's own type too for clarity.
- We should change `ComponentSparseSet` to take advantage of the new
niche. (This PR doesn't change that yet.)
- Consider removing or updating `Identifier`. This is only used for
`Entity`, so it might be worth combining since `Entity` is now more
unique.
---------
Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
# Objective
Provide a generic `impl SystemParam for Option<P>` that uses system
parameter validation. This immediately gives useful impls for params
like `EventReader` and `GizmosState` that are defined in terms of `Res`.
It also allows third-party system parameters to be usable with `Option`,
which was previously impossible due to orphan rules.
Note that this is a behavior change for `Option<Single>`. It currently
fails validation if there are multiple matching entities, but with this
change it will pass validation and produce `None`.
Also provide an impl for `Result<P, SystemParamValidationError>`. This
allows systems to inspect the error if necessary, either for bubbling it
up or for checking the `skipped` flag.
Fixes#12634Fixes#14949
Related to #18516
## Solution
Add generic `SystemParam` impls for `Option` and `Result`, and remove
the impls for specific types.
Update documentation and `fallible_params` example with the new
semantics for `Option<Single>`.
# Objective
- Fixes a subset of https://github.com/bevyengine/bevy/issues/13735 by
making `EntityRef`, `EntityMut` + similar WorldQueries use the system's
change ticks when being created from within a system.
In particular, this means that `entity_ref.get_ref::<T>()` will use the
correct change ticks (the ones from the system), which matches the
behaviour of querying for `Ref<T>` directly in the system parameters.
## Solution
- Implements the solution described by
https://github.com/bevyengine/bevy/issues/13735#issuecomment-2652482918
which is to add change ticks to the `UnsafeEntityCell`
## Testing
- Added a unit test that is close to what users would encounter: before
this PR the `Added`/`Changed` filters on `Ref`s created from `EntityRef`
are incorrect.
# Objective
A fair few items were deprecated in 0.16. Let's delete them now that
we're in the 0.17 development cycle!
## Solution
- Deleted items marked deprecated in 0.16.
## Testing
- CI
---
## Notes
I'm making the assumption that _everything_ deprecated in 0.16 should be
removed in 0.17. That may be a false assumption in certain cases. Please
check the items to be removed to see if there are any exceptions we
should keep around for another cycle!
# Objective
In #18301, `NonSendMarker` was defined in such a way that it actually
implements `Send`. This isn't strictly a soundness issue, as its goal is
to be used as a `SystemParam`, and it _does_ appropriately mark system
access as `!Send`. It just seems odd that `NonSendMarker: Send`.
## Solution
- Made `NonSendMarker` wrap `PhantomData<*mut ()>`, which forces it to
be `!Send`.
## Testing
- CI
---
## Notes
This does mean constructing a `NonSendMarker` _value_ will require using
the `SystemParam` trait, but I think that's acceptable as the marker as
a value should be rarely required if at all.
# Objective
Remaining work for and closes#17682. First half of work for that issue
was completed in [PR
17730](https://github.com/bevyengine/bevy/pull/17730). However, the rest
of the work ended up getting blocked because we needed a way of forcing
systems to run on the main thread without the use of `!Send` resources.
That was unblocked by [PR
18301](https://github.com/bevyengine/bevy/pull/18301).
This work should finish unblocking the resources-as-components effort.
# Testing
Ran several examples using my Linux machine, just to make sure things
are working as expected and no surprises pop up.
---------
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Fixes a part of #14274.
Bevy has an incredibly inconsistent naming convention for its system
sets, both internally and across the ecosystem.
<img alt="System sets in Bevy"
src="https://github.com/user-attachments/assets/d16e2027-793f-4ba4-9cc9-e780b14a5a1b"
width="450" />
*Names of public system set types in Bevy*
Most Bevy types use a naming of `FooSystem` or just `Foo`, but there are
also a few `FooSystems` and `FooSet` types. In ecosystem crates on the
other hand, `FooSet` is perhaps the most commonly used name in general.
Conventions being so wildly inconsistent can make it harder for users to
pick names for their own types, to search for system sets on docs.rs, or
to even discern which types *are* system sets.
To reign in the inconsistency a bit and help unify the ecosystem, it
would be good to establish a common recommended naming convention for
system sets in Bevy itself, similar to how plugins are commonly suffixed
with `Plugin` (ex: `TimePlugin`). By adopting a consistent naming
convention in first-party Bevy, we can softly nudge ecosystem crates to
follow suit (for types where it makes sense to do so).
Choosing a naming convention is also relevant now, as the [`bevy_cli`
recently adopted
lints](https://github.com/TheBevyFlock/bevy_cli/pull/345) to enforce
naming for plugins and system sets, and the recommended naming used for
system sets is still a bit open.
## Which Name To Use?
Now the contentious part: what naming convention should we actually
adopt?
This was discussed on the Bevy Discord at the end of last year, starting
[here](<https://discord.com/channels/691052431525675048/692572690833473578/1310659954683936789>).
`FooSet` and `FooSystems` were the clear favorites, with `FooSet` very
narrowly winning an unofficial poll. However, it seems to me like the
consensus was broadly moving towards `FooSystems` at the end and after
the poll, with Cart
([source](https://discord.com/channels/691052431525675048/692572690833473578/1311140204974706708))
and later Alice
([source](https://discord.com/channels/691052431525675048/692572690833473578/1311092530732859533))
and also me being in favor of it.
Let's do a quick pros and cons list! Of course these are just what I
thought of, so take it with a grain of salt.
`FooSet`:
- Pro: Nice and short!
- Pro: Used by many ecosystem crates.
- Pro: The `Set` suffix comes directly from the trait name `SystemSet`.
- Pro: Pairs nicely with existing APIs like `in_set` and
`configure_sets`.
- Con: `Set` by itself doesn't actually indicate that it's related to
systems *at all*, apart from the implemented trait. A set of what?
- Con: Is `FooSet` a set of `Foo`s or a system set related to `Foo`? Ex:
`ContactSet`, `MeshSet`, `EnemySet`...
`FooSystems`:
- Pro: Very clearly indicates that the type represents a collection of
systems. The actual core concept, system(s), is in the name.
- Pro: Parallels nicely with `FooPlugins` for plugin groups.
- Pro: Low risk of conflicts with other names or misunderstandings about
what the type is.
- Pro: In most cases, reads *very* nicely and clearly. Ex:
`PhysicsSystems` and `AnimationSystems` as opposed to `PhysicsSet` and
`AnimationSet`.
- Pro: Easy to search for on docs.rs.
- Con: Usually results in longer names.
- Con: Not yet as widely used.
Really the big problem with `FooSet` is that it doesn't actually
describe what it is. It describes what *kind of thing* it is (a set of
something), but not *what it is a set of*, unless you know the type or
check its docs or implemented traits. `FooSystems` on the other hand is
much more self-descriptive in this regard, at the cost of being a bit
longer to type.
Ultimately, in some ways it comes down to preference and how you think
of system sets. Personally, I was originally in favor of `FooSet`, but
have been increasingly on the side of `FooSystems`, especially after
seeing what the new names would actually look like in Avian and now
Bevy. I prefer it because it usually reads better, is much more clearly
related to groups of systems than `FooSet`, and overall *feels* more
correct and natural to me in the long term.
For these reasons, and because Alice and Cart also seemed to share a
preference for it when it was previously being discussed, I propose that
we adopt a `FooSystems` naming convention where applicable.
## Solution
Rename Bevy's system set types to use a consistent `FooSet` naming where
applicable.
- `AccessibilitySystem` → `AccessibilitySystems`
- `GizmoRenderSystem` → `GizmoRenderSystems`
- `PickSet` → `PickingSystems`
- `RunFixedMainLoopSystem` → `RunFixedMainLoopSystems`
- `TransformSystem` → `TransformSystems`
- `RemoteSet` → `RemoteSystems`
- `RenderSet` → `RenderSystems`
- `SpriteSystem` → `SpriteSystems`
- `StateTransitionSteps` → `StateTransitionSystems`
- `RenderUiSystem` → `RenderUiSystems`
- `UiSystem` → `UiSystems`
- `Animation` → `AnimationSystems`
- `AssetEvents` → `AssetEventSystems`
- `TrackAssets` → `AssetTrackingSystems`
- `UpdateGizmoMeshes` → `GizmoMeshSystems`
- `InputSystem` → `InputSystems`
- `InputFocusSet` → `InputFocusSystems`
- `ExtractMaterialsSet` → `MaterialExtractionSystems`
- `ExtractMeshesSet` → `MeshExtractionSystems`
- `RumbleSystem` → `RumbleSystems`
- `CameraUpdateSystem` → `CameraUpdateSystems`
- `ExtractAssetsSet` → `AssetExtractionSystems`
- `Update2dText` → `Text2dUpdateSystems`
- `TimeSystem` → `TimeSystems`
- `AudioPlaySet` → `AudioPlaybackSystems`
- `SendEvents` → `EventSenderSystems`
- `EventUpdates` → `EventUpdateSystems`
A lot of the names got slightly longer, but they are also a lot more
consistent, and in my opinion the majority of them read much better. For
a few of the names I took the liberty of rewording things a bit;
definitely open to any further naming improvements.
There are still also cases where the `FooSystems` naming doesn't really
make sense, and those I left alone. This primarily includes system sets
like `Interned<dyn SystemSet>`, `EnterSchedules<S>`, `ExitSchedules<S>`,
or `TransitionSchedules<S>`, where the type has some special purpose and
semantics.
## Todo
- [x] Should I keep all the old names as deprecated type aliases? I can
do this, but to avoid wasting work I'd prefer to first reach consensus
on whether these renames are even desired.
- [x] Migration guide
- [x] Release notes
I can't underrate anisotropic filtering.
# Objective
- Allow easily enabling anisotropic filtering on glTF assets.
- Allow selecting `ImageFilterMode` for glTF assets at runtime.
## Solution
- Added a Resource `DefaultGltfImageSampler`: it stores
`Arc<Mutex<ImageSamplerDescriptor>>` and the same `Arc` is stored in
`GltfLoader`. The default is independent from provided to `ImagePlugin`
and is set in the same way but with `GltfPlugin`. It can then be
modified at runtime with `DefaultGltfImageSampler::set`.
- Added two fields to `GltfLoaderSettings`: `default_sampler:
Option<ImageSamplerDescriptor>` to override aforementioned global
default descriptor and `override_sampler: bool` to ignore glTF sampler
data.
## Showcase
Enabling anisotropic filtering as easy as:
```rust
app.add_plugins(DefaultPlugins.set(GltfPlugin{
default_sampler: ImageSamplerDescriptor {
min_filter: ImageFilterMode::Linear,
mag_filter: ImageFilterMode::Linear,
mipmap_filter: ImageFilterMode::Linear,
anisotropy_clamp: 16,
..default()
},
..default()
}))
```
Use code below to ignore both the global default sampler and glTF data,
having `your_shiny_sampler` used directly for all textures instead:
```rust
commands.spawn(SceneRoot(asset_server.load_with_settings(
GltfAssetLabel::Scene(0).from_asset("models/test-scene.gltf"),
|settings: &mut GltfLoaderSettings| {
settings.default_sampler = Some(your_shiny_sampler);
settings.override_sampler = true;
}
)));
```
Remove either setting to get different result! They don't come in pair!
Scene rendered with trillinear texture filtering:

Scene rendered with 16x anisotropic texture filtering:

## Migration Guide
- The new fields in `GltfLoaderSettings` have their default values
replicate previous behavior.
---------
Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com>
# Objective
Originally [provided as a solution to a user's problem in
Discord](https://discord.com/channels/691052431525675048/1247654592838111302/1344431131277394042),
library authors might find the need to present user-registered systems
with system-specific data. Typically `Local<T>` is used for this type of
thing, but its not generally feasible or possible to configure/set the
underlying `T` data for locals. Alternatively, we can use `SystemInput`
to pass the data.
## Solution
- Added `IntoSystem::with_input`: Allows system-specific data to be
passed in explicitly.
- Added `IntoSystem::with_input_from`: Allows system-specific data to be
created at initialization time via `FromWorld`.
## Testing
Added two new tests, testing each of `with_input` and `with_input_from`.
# Objective
With the current `MapEntities` `impl`s, it is not possible to derive
things like this:
```rust
#[derive(Component)]
pub struct Inventory {
#[entities]
slots: Vec<Option<Entity>>,
}
```
This is because `MapEntities` is only implemented for `Vec<Entity>` &
`Option<Entity>`, and not arbitrary combinations of those.
It would be nice to also support those types.
## Solution
I replaced the `impl`s of the following types
- `Option<Entity>`: replaced with `Option<T>`
- `Vec<Entity>`: replaced with `Vec<T>`
- `HashSet<Entity, S>`: replaced with `HashSet<T, S>`
- `T` also had to be `Eq + core:#️⃣:Hash` here. **Not sure if this is
too restrictive?**
- `IndexSet<Entity, S>`: replaced with `IndexSet <T, S>`
- `T` also had to be `Eq + core:#️⃣:Hash` here. **Not sure if this is
too restrictive?**
- `BTreeSet<Entity>`: replaced with `BTreeSet<T>`
- `VecDeque<Entity>`: replaced with `VecDeque<T>`
- `SmallVec<A: smallvec::Array<Item = Entity>>`: replaced with
`SmallVec<A: smallvec::Array<Item = T>>`
(in all of the above, `T` is a generic type that implements
`MapEntities` (`Entity` being one of them).)
## Testing
I did not test any of this, but extended the `Component::map_entities`
doctest with an example usage of the newly supported types.
---
## Showcase
With these changes, this is now possible:
```rust
#[derive(Component)]
pub struct Inventory {
#[entities]
slots: Vec<Option<Entity>>,
}
```
# Objective
`RelatedSpawnerCommands` offers methods to get the underlying
`Commands`.
`RelatedSpawner` does not expose the inner `World` reference so far.
I currently want to write extension traits for both of them but I need
to duplicate the whole API for the latter because I cannot get it's
`&mut World`.
## Solution
Add methods for immutable and mutable `World` access
# Objective
Currently, `bevy_ecs`'s `children!` macro only supports spawning up to
twelve children at once. Ideally there would be no limit.
## Solution
`children!` is limited because `SpawnableList`, [the primary trait bound
here](https://docs.rs/bevy/0.16.0-rc.5/bevy/ecs/hierarchy/struct.Children.html#method.spawn),
uses the fake variadics pattern on tuples of up to twelve elements.
However, since a tuple itself implements `SpawnableList`, we can simply
nest tuples of entities when we run out of room.
This PR achieves this using `macro_rules` macros with a bit of brute
force, following [some discussion on
Discord](https://discord.com/channels/691052431525675048/692572690833473578/1362174415458013314).
If we create patterns for lists of up to eleven bundles, then use a
repetition pattern to handle the rest, we can "special-case" the
recursion into a nested tuple.
In principle, this would permit an arbitrary number of children, but
Rust's recursion limits will cut things short at around 1400 elements by
default. Of course, it's generally not a good idea to stick that many
bundles in a single invocation, but it might be worth mentioning in the
docs.
## Implementation notes
### Why are cases 0-11 expanded by hand?
We could make use of a tertiary macro:
```rs
macro_rules! recursive_spawn {
// so that this...
($a:expr, $b:expr) => {
(
$crate::spawn::Spawn($a),
$crate::spawn::Spawn($b),
)
};
// becomes this...
($a:expr, $b:expr) => {
$crate::spawn_tuple!($a, $b)
};
}
```
But I already feel a little bad exporting `recursive_spawn`. I'd really
like to avoid exposing more internals, even if they are annotated with
`#[doc(hidden)]`. If I had to guess, I'd say it'll also make the
expansion a tiny bit slower.
### Do we really need to handle up to twelve elements in the macro?
The macro is a little long, but doing it this way maximizes the
"flatness" of the types to be spawned. This should improve the codegen a
bit and makes the macro output a little bit easier to look at.
## Future work
The `related!` macro is essentially the same as `children!`, so if this
direction is accepted, `related!` should receive the same treatment. I
imagine we'd want to extract out the `recursive_spawn` macro into its
own file since it can be used for both. If this should be tackled in
this PR, let me know!
## Testing
This change is fairly trivial, but I added a single test to verify that
it compiles and nothing goes wrong once recursion starts happening. It's
pretty easy to verify that the change works in practice -- just spawn
over twelve entities as children at once!
# Objective
- Acts on certain elements of #18799
- Closes#1615
- New baseline for #18170
## Solution
- Created a new `cfg` module in `bevy_platform` which contains two
macros to aid in working with features like `web`, `std`, and `alloc`.
- `switch` is a stable implementation of
[`cfg_match`](https://doc.rust-lang.org/std/macro.cfg_match.html), which
itself is a `core` alternative to [`cfg_if`](https://docs.rs/cfg-if).
- `define_alias` is a `build.rs`-free alternative to
[`cfg_aliases`](https://docs.rs/cfg_aliases) with the ability to share
feature information between crates.
- Switched to these macros within `bevy_platform` to demonstrate usage.
## Testing
- CI
---
## Showcase
Consider the typical `std` feature as an example of a "virality". With
just `bevy_platform`, `bevy_utils`, and `bevy_ecs`, we have 3 crates in
a chain where activating `std` in any of them should really activate it
everywhere. The status-quo for this is for each crate to define its own
`std` feature, and ensure it includes the `std` feature of every
dependency in that feature. For crates which don't even interact with
`std` directly, this can be quite cumbersome. Especially considering
that Bevy has a fundamental crate, `bevy_platform`, which is a
dependency for effectively every crate.
Instead, we can use `define_alias` to create a macro which will
conditionally compile code if and only if the specified configuration
condition is met _in the defining crate_.
```rust
// In `bevy_platform`
define_alias! {
#[cfg(feature = "std")] => {
/// Indicates the `std` crate is available and can be used.
std
}
#[cfg(all(target_arch = "wasm32", feature = "web"))] => {
/// Indicates that this target has access to browser APIs.
web
}
}
```
The above `web` and `std` macros will either no-op the provided code if
the conditions are not met, or pass it unmodified if it is met. Since it
is evaluated in the context of the defining crate, `bevy_platform/std`
can be used to conditionally compile code in `bevy_utils` and `bevy_ecs`
_without_ those crates including their own `std` features.
```rust
// In `bevy_utils`
use bevy_platform::cfg;
// If `bevy_platform` has `std`, then we can too!
cfg::std! {
extern crate std;
}
```
To aid in more complex configurations, `switch` is provided to provide a
`cfg_if` alternative that is compatible with `define_alias`:
```rust
use bevy_platform::cfg;
cfg::switch! {
#[cfg(feature = "foo")] => { /* use the foo API */ }
cfg::web => { /* use browser API */ }
cfg::std => { /* use std */ }
_ => { /* use a fallback implementation */ }
}
```
This paradigm would allow Bevy's sub-crates to avoid re-exporting viral
features, and also enable functionality in response to availability in
their dependencies, rather than from explicit features (bottom-up
instead of top-down). I imagine that a "full rollout" of this paradigm
would remove most viral features from Bevy's crates, leaving only
`bevy_platform`, `bevy_internal`, and `bevy` (since `bevy`/`_internal`
are explicitly re-exports of all of Bevy's crates).
This bottom-up approach may be useful in other areas of Bevy's features
too. For example, `bevy_core_pipeline/tonemapping_luts` requires:
- bevy_render/ktx2
- bevy_image/ktx2
- bevy_image/zstd
If `define_alias` was used in `bevy_image`, `bevy_render` would not need
to re-export the `ktx2` feature, and `bevy_core_pipeline` could directly
probe `bevy_image` for the status of `ktx2` and `zstd` features to
determine if it should compile the `tonemapping_luts` functionality,
rather than having an explicitly feature. Of course, an explicit feature
is still important for _features_, so this may not be the best example,
but it highlights that with this paradigm crates can reactively provide
functionality, rather than needing to proactively declare feature
combinations up-front and hope the user enables them.
---------
Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
# Objective
- Alternative to and builds on top of #16284.
- Fixes#15849.
## Solution
- Rename component `StateScoped` to `DespawnOnExitState`.
- Rename system `clear_state_scoped_entities` to
`despawn_entities_on_exit_state`.
- Add `DespawnOnEnterState` and `despawn_entities_on_enter_state` which
is the `OnEnter` equivalent.
> [!NOTE]
> Compared to #16284, the main change is that I did the rename in such a
way as to keep the terms `OnExit` and `OnEnter` together. In my own
game, I was adding `VisibleOnEnterState` and `HiddenOnExitState` and
when naming those, I kept the `OnExit` and `OnEnter` together. When I
checked #16284 it stood out to me that the naming was a bit awkward.
Putting the `State` in the middle and breaking up `OnEnter` and `OnExit`
also breaks searching for those terms.
## Open questions
1. Should we split `enable_state_scoped_entities` into two functions,
one for the `OnEnter` and one for the `OnExit`? I personally have zero
need thus far for the `OnEnter` version, so I'd be interested in not
having this enabled unless I ask for it.
2. If yes to 1., should we follow my lead in my `Visibility` state
components (see below) and name these
`app.enable_despawn_entities_on_enter_state()` and
`app.enable_despawn_entities_on_exit_state()`, which IMO says what it
does on the tin?
## Testing
Ran all changed examples.
## Side note: `VisibleOnEnterState` and `HiddenOnExitState`
For reference to anyone else and to help with the open questions, I'm
including the code I wrote for controlling entity visibility when a
state is entered/exited.
<details>
<summary>visibility.rs</summary>
```rust
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_reflect::prelude::*;
use bevy_render::prelude::*;
use bevy_state::{prelude::*, state::StateTransitionSteps};
use tracing::*;
pub trait AppExtStates {
fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self;
fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self;
}
impl AppExtStates for App {
fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self {
self.main_mut()
.enable_visible_entities_on_enter_state::<S>();
self
}
fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self {
self.main_mut().enable_hidden_entities_on_exit_state::<S>();
self
}
}
impl AppExtStates for SubApp {
fn enable_visible_entities_on_enter_state<S: States>(&mut self) -> &mut Self {
if !self
.world()
.contains_resource::<Events<StateTransitionEvent<S>>>()
{
let name = core::any::type_name::<S>();
warn!("Visible entities on enter state are enabled for state `{}`, but the state isn't installed in the app!", name);
}
// We work with [`StateTransition`] in set
// [`StateTransitionSteps::ExitSchedules`] as opposed to [`OnExit`],
// because [`OnExit`] only runs for one specific variant of the state.
self.add_systems(
StateTransition,
update_to_visible_on_enter_state::<S>.in_set(StateTransitionSteps::ExitSchedules),
)
}
fn enable_hidden_entities_on_exit_state<S: States>(&mut self) -> &mut Self {
if !self
.world()
.contains_resource::<Events<StateTransitionEvent<S>>>()
{
let name = core::any::type_name::<S>();
warn!("Hidden entities on exit state are enabled for state `{}`, but the state isn't installed in the app!", name);
}
// We work with [`StateTransition`] in set
// [`StateTransitionSteps::ExitSchedules`] as opposed to [`OnExit`],
// because [`OnExit`] only runs for one specific variant of the state.
self.add_systems(
StateTransition,
update_to_hidden_on_exit_state::<S>.in_set(StateTransitionSteps::ExitSchedules),
)
}
}
#[derive(Clone, Component, Debug, Reflect)]
#[reflect(Component, Debug)]
pub struct VisibleOnEnterState<S: States>(pub S);
#[derive(Clone, Component, Debug, Reflect)]
#[reflect(Component, Debug)]
pub struct HiddenOnExitState<S: States>(pub S);
/// Makes entities marked with [`VisibleOnEnterState<S>`] visible when the state
/// `S` is entered.
pub fn update_to_visible_on_enter_state<S: States>(
mut transitions: EventReader<StateTransitionEvent<S>>,
mut query: Query<(&VisibleOnEnterState<S>, &mut Visibility)>,
) {
// We use the latest event, because state machine internals generate at most
// 1 transition event (per type) each frame. No event means no change
// happened and we skip iterating all entities.
let Some(transition) = transitions.read().last() else {
return;
};
if transition.entered == transition.exited {
return;
}
let Some(entered) = &transition.entered else {
return;
};
for (binding, mut visibility) in query.iter_mut() {
if binding.0 == *entered {
visibility.set_if_neq(Visibility::Visible);
}
}
}
/// Makes entities marked with [`HiddenOnExitState<S>`] invisible when the state
/// `S` is exited.
pub fn update_to_hidden_on_exit_state<S: States>(
mut transitions: EventReader<StateTransitionEvent<S>>,
mut query: Query<(&HiddenOnExitState<S>, &mut Visibility)>,
) {
// We use the latest event, because state machine internals generate at most
// 1 transition event (per type) each frame. No event means no change
// happened and we skip iterating all entities.
let Some(transition) = transitions.read().last() else {
return;
};
if transition.entered == transition.exited {
return;
}
let Some(exited) = &transition.exited else {
return;
};
for (binding, mut visibility) in query.iter_mut() {
if binding.0 == *exited {
visibility.set_if_neq(Visibility::Hidden);
}
}
}
```
</details>
---------
Co-authored-by: Benjamin Brienen <Benjamin.Brienen@outlook.com>
Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
# Objective
Contributes to #18741 and #18453.
## Solution
Deprecate `SimpleExecutor`. If users run into migration issues, we can
backtrack. Otherwise, we follow this up with #18741
We can't easily deprecate the module too because of
[this](https://github.com/rust-lang/rust/issues/47238).
## Testing
CI
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Cyrill Schenkel <cyrill.schenkel@gmail.com>
# Objective
- bevy removed `Observe` type parameters in #15151 ,it enables merging
`Observer` and `ObserverState ` into a single component. with this
consolidation ,we can improve efficiency while reducing boilerplate.
## Solution
- remove `ObserverState `and merge it into `Observer`
## Testing
40%~60% performance win due to removal of redundant look up.

This also improves ergonomics when using dynamic observer
```rust
// previously
world.spawn(ObserverState {
// SAFETY: we registered `event_a` above and it matches the type of EventA
descriptor: unsafe { ObserverDescriptor::default().with_events(vec![event_a]) },
runner: |mut world, _trigger, _ptr, _propagate| {
world.resource_mut::<Order>().observed("event_a");
},
..Default::default()
});
// now
let observe = unsafe {
Observer::with_dynamic_runner(|mut world, _trigger, _ptr, _propagate| {
world.resource_mut::<Order>().observed("event_a");
})
.with_event(event_a)
};
world.spawn(observe);
```
# Objective
Simplify code in the `SingleThreadedExecutor` by removing a special case
for exclusive systems.
The `SingleThreadedExecutor` runs systems without immediately applying
deferred buffers. That required calling `run_unsafe()` instead of
`run()`, but that would `panic` for exclusive systems, so the code also
needed a special case for those. Following #18076 and #18406, we have a
`run_without_applying_deferred` method that has the exact behavior we
want and works on exclusive systems.
## Solution
Replace the code in `SingleThreadedExecutor` that runs systems with a
single call to `run_without_applying_deferred()`. Also add this as a
wrapper in the `__rust_begin_short_backtrace` module to preserve the
special behavior for backtraces.
# Objective
Colors currently do not implement `StableInterpolate`, which makes them
ineligible for functions like `smooth_nudge` and make some generic APIs
awkward.
## Solution
Implemented `StableInterpolate` for linear color types that should be
uncontroversial. Non-linear types like `Hsl` are not implemented in this
PR.
## Testing
Added a test that checks implementations are correct.
# Objective
- Minor cleanup.
- This seems to have been introduced in #8336. There is no discussion
about it I can see, there's no comment explaining why this is here and
not in `ScreenshotPlugin`. This seems to have just been misplaced.
## Solution
- Move this to the ScreenshotPlugin!
## Testing
- The screenshot example still works at least on desktop.
# Objective
It has long been a todo item in the ecs to create a `BundleRemover`
alongside the inserter, spawner, etc.
This is an uncontroversial first step of #18514.
## Solution
Move existing code from complex helper functions to one generalized
`BundleRemover`.
## Testing
Existing tests.
# Objective
Let `FilteredEntityRef` and `FilteredEntityMut` receive access when
nested inside tuples or `#[derive(QueryData)]` types. Make sure to
exclude any access that would conflict with other subqueries!
Fixes#14349
## Solution
Replace `WorldQuery::set_access(state, access)` with a new method,
`QueryData::provide_extra_access(state, access, available_access)`, that
passes both the total available access and the currently used access.
This is called after `WorldQuery::update_component_access()`, so any
access used by ordinary subqueries will be known. `FilteredEntityRef`
and `FilteredEntityMut` can use the combination to determine how much
access they can safely take, while tuples can safely pass those
parameters directly to their subqueries.
This requires a new `Access::remove_conflicting_access()` method that
can be used to remove any access that would conflict with existing
access. Implementing this method was easier by first factoring some
common set manipulation code out of `Access::extend`. I can extract that
refactoring to a separate PR if desired.
Have `FilteredEntity(Ref|Mut)` store `Access` instead of
`FilteredAccess` because they do not need to keep track of the filter.
This was necessary in an early draft but no longer is. I left it in
because it's small and I'm touching that code anyway, but I can extract
it to a separate PR if desired.
# Objective
Fixes#17803
## Solution
- Add an `Allows<T>` `QueryFilter` that adds archetypal access for `T`
- Fix access merging to include archetypal from both sides
## Testing
- Added a case to the unit test for the application of
`DefaultQueryFilters`
# Objective
Based on and closes#18054, this PR builds on #18035 and #18147 to
remove:
- `Commands::insert_or_spawn_batch`
- `Entities::alloc_at_without_replacement`
- `Entities::alloc_at`
- `entity::AllocAtWithoutReplacement`
- `World::insert_or_spawn_batch`
- `World::insert_or_spawn_batch_with_caller`
## Testing
Just removing unused, deprecated code, so no new tests. Note that as of
writing, #18035 is still under testing and review.
## Future Work
Per
[this](https://github.com/bevyengine/bevy/issues/18054#issuecomment-2689088899)
comment on #18054, there may be additional performance improvements
possible to the entity allocator now that `alloc_at` no longer is
supported. At a glance, I don't see anything obvious to improve, but it
may be worth further investigation in the future.
---------
Co-authored-by: JaySpruce <jsprucebruce@gmail.com>
The LogPlugin now allows overriding the default
`tracing_subscriber::fmt::Layer` through a new `fmt_layer` option. This
enables customization of the default log output format without having to
replace the entire logging system.
For example, to disable timestamps in the log output:
```rust
fn fmt_layer(_app: &mut App) -> Option<bevy::log::BoxedFmtLayer> {
Some(Box::new(
bevy::log::tracing_subscriber::fmt::Layer::default()
.without_time()
.with_writer(std::io::stderr),
))
}
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(bevy::log::LogPlugin {
fmt_layer,
..default()
}))
.run();
}
```
This is different from the existing `custom_layer` option, because that
option _adds_ additional layers to the subscriber, but can't modify the
default formatter layer (at least, not to my knowledge).
I almost always disable timestamps in my Bevy logs, and usually also
tweak other default log formatting (such as `with_span_events`), which
made it so that I always had to disable the default logger. This allows
me to use everything the Bevy logger supports (including tracy support),
while still formatting the default logs the way I like them.
---------
Signed-off-by: Jean Mertz <git@jeanmertz.com>
# Objective
Add a viewport widget.
## Solution
- Add a new `ViewportNode` component to turn a UI node into a viewport.
- Add `viewport_picking` to pass pointer inputs from other pointers to
the viewport's pointer.
- Notably, this is somewhat functionally different from the viewport
widget in [the editor
prototype](https://github.com/bevyengine/bevy_editor_prototypes/pull/110/files#L124),
which just moves the pointer's location onto the render target. Viewport
widgets have their own pointers.
- Care is taken to handle dragging in and out of viewports.
- Add `update_viewport_render_target_size` to update the viewport node's
render target's size if the node size changes.
- Feature gate picking-related viewport items behind
`bevy_ui_picking_backend`.
## Testing
I've been using an example I made to test the widget (and added it as
`viewport_node`):
<details><summary>Code</summary>
```rust
//! A simple scene to demonstrate spawning a viewport widget. The example will demonstrate how to
//! pick entities visible in the widget's view.
use bevy::picking::pointer::PointerInteraction;
use bevy::prelude::*;
use bevy::ui::widget::ViewportNode;
use bevy::{
image::{TextureFormatPixelInfo, Volume},
window::PrimaryWindow,
};
use bevy_render::{
camera::RenderTarget,
render_resource::{
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
},
};
fn main() {
App::new()
.add_plugins((DefaultPlugins, MeshPickingPlugin))
.add_systems(Startup, test)
.add_systems(Update, draw_mesh_intersections)
.run();
}
#[derive(Component, Reflect, Debug)]
#[reflect(Component)]
struct Shape;
fn test(
mut commands: Commands,
window: Query<&Window, With<PrimaryWindow>>,
mut images: ResMut<Assets<Image>>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// Spawn a UI camera
commands.spawn(Camera3d::default());
// Set up an texture for the 3D camera to render to
let window = window.get_single().unwrap();
let window_size = window.physical_size();
let size = Extent3d {
width: window_size.x,
height: window_size.y,
..default()
};
let format = TextureFormat::Bgra8UnormSrgb;
let image = Image {
data: Some(vec![0; size.volume() * format.pixel_size()]),
texture_descriptor: TextureDescriptor {
label: None,
size,
dimension: TextureDimension::D2,
format,
mip_level_count: 1,
sample_count: 1,
usage: TextureUsages::TEXTURE_BINDING
| TextureUsages::COPY_DST
| TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
},
..default()
};
let image_handle = images.add(image);
// Spawn the 3D camera
let camera = commands
.spawn((
Camera3d::default(),
Camera {
// Render this camera before our UI camera
order: -1,
target: RenderTarget::Image(image_handle.clone().into()),
..default()
},
))
.id();
// Spawn something for the 3D camera to look at
commands
.spawn((
Mesh3d(meshes.add(Cuboid::new(5.0, 5.0, 5.0))),
MeshMaterial3d(materials.add(Color::WHITE)),
Transform::from_xyz(0.0, 0.0, -10.0),
Shape,
))
// We can observe pointer events on our objects as normal, the
// `bevy::ui::widgets::viewport_picking` system will take care of ensuring our viewport
// clicks pass through
.observe(on_drag_cuboid);
// Spawn our viewport widget
commands
.spawn((
Node {
position_type: PositionType::Absolute,
top: Val::Px(50.0),
left: Val::Px(50.0),
width: Val::Px(200.0),
height: Val::Px(200.0),
border: UiRect::all(Val::Px(5.0)),
..default()
},
BorderColor(Color::WHITE),
ViewportNode::new(camera),
))
.observe(on_drag_viewport);
}
fn on_drag_viewport(drag: Trigger<Pointer<Drag>>, mut node_query: Query<&mut Node>) {
if matches!(drag.button, PointerButton::Secondary) {
let mut node = node_query.get_mut(drag.target()).unwrap();
if let (Val::Px(top), Val::Px(left)) = (node.top, node.left) {
node.left = Val::Px(left + drag.delta.x);
node.top = Val::Px(top + drag.delta.y);
};
}
}
fn on_drag_cuboid(drag: Trigger<Pointer<Drag>>, mut transform_query: Query<&mut Transform>) {
if matches!(drag.button, PointerButton::Primary) {
let mut transform = transform_query.get_mut(drag.target()).unwrap();
transform.rotate_y(drag.delta.x * 0.02);
transform.rotate_x(drag.delta.y * 0.02);
}
}
fn draw_mesh_intersections(
pointers: Query<&PointerInteraction>,
untargetable: Query<Entity, Without<Shape>>,
mut gizmos: Gizmos,
) {
for (point, normal) in pointers
.iter()
.flat_map(|interaction| interaction.iter())
.filter_map(|(entity, hit)| {
if !untargetable.contains(*entity) {
hit.position.zip(hit.normal)
} else {
None
}
})
{
gizmos.arrow(point, point + normal.normalize() * 0.5, Color::WHITE);
}
}
```
</details>
## Showcase
https://github.com/user-attachments/assets/39f44eac-2c2a-4fd9-a606-04171f806dc1
## Open Questions
- <del>Not sure whether the entire widget should be feature gated behind
`bevy_ui_picking_backend` or not? I chose a partial approach since maybe
someone will want to use the widget without any picking being
involved.</del>
- <del>Is `PickSet::Last` the expected set for `viewport_picking`?
Perhaps `PickSet::Input` is more suited.</del>
- <del>Can `dragged_last_frame` be removed in favor of a better dragging
check? Another option that comes to mind is reading `Drag` and `DragEnd`
events, but this seems messier.</del>
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
# Objective
Stop using `ArchetypeComponentId` in the executor. These IDs will grow
even more quickly with relations, and the size may start to degrade
performance.
## Solution
Have systems expose their `FilteredAccessSet<ComponentId>`, and have the
executor use that to determine which systems conflict. This can be
determined statically, so determine all conflicts during initialization
and only perform bit tests when running.
## Testing
I ran many_foxes and didn't see any performance changes. It's probably
worth testing this with a wider range of realistic schedules to see
whether the reduced concurrency has a cost in practice, but I don't know
what sort of test cases to use.
## Migration Guide
The schedule will now prevent systems from running in parallel if there
*could* be an archetype that they conflict on, even if there aren't
actually any. For example, these systems will now conflict even if no
entity has both `Player` and `Enemy` components:
```rust
fn player_system(query: Query<(&mut Transform, &Player)>) {}
fn enemy_system(query: Query<(&mut Transform, &Enemy)>) {}
```
To allow them to run in parallel, use `Without` filters, just as you
would to allow both queries in a single system:
```rust
// Either one of these changes alone would be enough
fn player_system(query: Query<(&mut Transform, &Player), Without<Enemy>>) {}
fn enemy_system(query: Query<(&mut Transform, &Enemy), Without<Player>>) {}
```
# Objective
- When loading a folder with dot files inside, Bevy crashes:
```
thread 'IO Task Pool (1)' panicked at crates/bevy_asset/src/io/mod.rs:260:10:
asset paths must have extensions
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
- those files are common for other tools to store their
settings/metadata
## Solution
- Ignore files starting with a dot when loading folders
# Objective
Prevent using exclusive systems as observers. Allowing them is unsound,
because observers are only expected to have `DeferredWorld` access, and
the observer infrastructure will keep pointers that are invalidated by
the creation of `&mut World`.
See
https://github.com/bevyengine/bevy/actions/runs/14778342801/job/41491517847?pr=19011
for a MIRI failure in a recent PR caused by an exclusive system being
used as an observer in a test.
## Solution
Have `Observer::new` panic if `System::is_exclusive()` is true. Document
that method, and methods that call it, as panicking.
(It should be possible to express this in the type system so that the
calls won't even compile, but I did not want to attempt that.)
## Testing
Added a unit test that calls `World::add_observer` with an exclusive
system.
# Objective
I've been tinkering with ECS insertion/removal lately, and noticed that
sparse sets just... don't interact with `InsertMode` at all. Sure
enough, using `insert_if_new` with a sparse component does the same
thing as `insert`.
# Solution
- Add a check in `BundleInfo::write_components` to drop the new value if
the entity already has the component and `InsertMode` is `Keep`.
- Add necessary methods to sparse set internals to fetch the drop
function.
# Testing
Minimal reproduction:
<details>
<summary>Code</summary>
```
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(PostStartup, component_print)
.run();
}
#[derive(Component)]
#[component(storage = "SparseSet")]
struct SparseComponent(u32);
fn setup(mut commands: Commands) {
let mut entity = commands.spawn_empty();
entity.insert(SparseComponent(1));
entity.insert(SparseComponent(2));
let mut entity = commands.spawn_empty();
entity.insert(SparseComponent(3));
entity.insert_if_new(SparseComponent(4));
}
fn component_print(query: Query<&SparseComponent>) {
for component in &query {
info!("{}", component.0);
}
}
```
</details>
Here it is on Bevy Playground (0.15.3):
https://learnbevy.com/playground?share=2a96a68a81e804d3fdd644a833c1d51f7fa8dd33fc6192fbfd077b082a6b1a41
Output on `main`:
```
2025-05-04T17:50:50.401328Z INFO system{name="fork::component_print"}: fork: 2
2025-05-04T17:50:50.401583Z INFO system{name="fork::component_print"}: fork: 4
```
Output with this PR :
```
2025-05-04T17:51:33.461835Z INFO system{name="fork::component_print"}: fork: 2
2025-05-04T17:51:33.462091Z INFO system{name="fork::component_print"}: fork: 3
```
# Objective
- Contributes to #18978
## Solution
- Disable default features on all dependencies in `bevy_asset` and
explicitly enable ones that are required.
- Remove `compile_error` caused by enabling `file_watcher` without
`multi_threaded` by including `multi_threaded` in `file_watcher`.
## Testing
- CI
---
## Notes
No breaking changes here, just a little cleaning before the more
controversial changes for `no_std` support.
---------
Co-authored-by: François Mockers <mockersf@gmail.com>
# Objective
Fixes#18969
## Solution
Also updated `Aabb3d` implementation for consistency.
## Testing
Added tests for `Aabb2d` and `Aabb3d` to verify correct rotation
behavior for angles greater than 90 degrees.
# Objective
`BTreeSet` doesn't implement `RelationshipSourceCollection`.
## Solution
Implement it.
## Testing
`cargo clippy`
---
## Showcase
You can now use `BTreeSet` in a `RelationshipTarget`
---------
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Co-authored-by: François Mockers <mockersf@gmail.com>
# Objective
Create a `When` system param wrapper for skipping systems that fail
validation.
Currently, the `Single` and `Populated` parameters cause systems to skip
when they fail validation, while the `Res` family causes systems to
error. Generalize this so that any fallible parameter can be used either
to skip a system or to raise an error. A parameter used directly will
always raise an error, and a parameter wrapped in `When<P>` will always
cause the system to be silently skipped.
~~Note that this changes the behavior for `Single` and `Populated`. The
current behavior will be available using `When<Single>` and
`When<Populated>`.~~
Fixes#18516
## Solution
Create a `When` system param wrapper that wraps an inner parameter and
converts all validation errors to `skipped`.
~~Change the behavior of `Single` and `Populated` to fail by default.~~
~~Replace in-engine use of `Single` with `When<Single>`. I updated the
`fallible_systems` example, but not all of the others. The other
examples I looked at appeared to always have one matching entity, and it
seemed more clear to use the simpler type in those cases.~~
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
Co-authored-by: François Mockers <mockersf@gmail.com>
# Objective
- If using a `NonNilUuid` in Bevy, it's difficult to reflect it.
## Solution
- Adds `NonNilUuid` using `impl_reflect_opaque!`.
## Testing
- Built with no issues found locally.
- Essentially the same as the `Uuid` support except without `Default`.
Co-authored-by: TM Storey <mail@tmstorey.id.au>
# Objective
Add background colors for text.
Fixes#18889
## Solution
New component `TextBackgroundColor`, add it to any UI `Text` or
`TextSpan` entity to add a background color to its text.
New field on `TextLayoutInfo` `section_rects` holds the list of bounding
rects for each text section.
The bounding rects are generated in `TextPipeline::queue_text` during
text layout, `extract_text_background_colors` extracts the colored
background rects for rendering.
Didn't include `Text2d` support because of z-order issues.
The section rects can also be used to implement interactions targeting
individual text sections.
## Testing
Includes a basic example that can be used for testing:
```
cargo run --example text_background_colors
```
---
## Showcase

Using a proportional font with kerning the results aren't so tidy (since
the bounds of adjacent glyphs can overlap) but it still works fine:

---------
Co-authored-by: Olle Lukowski <lukowskiolle@gmail.com>
Co-authored-by: Gilles Henaux <ghx_github_priv@fastmail.com>
Adopted #13869
# Objective
Fixes#9076
## Solution
Using `rodio`'s `try_seek`
## Testing
@ivanstepanovftw added a `seek` system using `AudioSink` to the
`audio_control.rs` example. I got it working with .mp3 files, but rodio
doesn't support seeking for .ogg and .flac files, so I removed it from
the commit (since the assets folder only has .ogg files). Another thing
to note is that `try_seek` fails when using `PlaybackMode::Loop`, as
`rodio::source::buffered::Buffered` doesn't support `try_seek`. I
haven't tested `SpatialAudioSink`.
## Notes
I copied the docs for `try_seek` verbatim from `rodio`, and re-exported
`rodio::source::SeekError`. I'm not completely confident in those
decisions, please let me know if I'm doing anything wrong.
</details>
---------
Co-authored-by: Ivan Stepanov <ivanstepanovftw@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
Co-authored-by: François Mockers <mockersf@gmail.com>
# Objective
The default should be `OverflowClipBox::PaddingBox` not
`OverflowClipBox::ContentBox`
`padding-box` is the default in CSS.
## Solution
Set the default to `PaddingBox`.
## Testing
Compare the `overflow` UI example on main vs with this PR. You should
see that on main the outline around the inner node gets clipped. With
this PR by default clipping starts at the inner edge of the border (the
`padding-box`) and the outlines are visible.
Fixes#18934
# Objective
We have methods to:
- Add related entities
- Replace related entities
- Remove specific related entities
We don't have a method the remove all related entities so.
## Solution
Add a method to remove all related entities.
## Testing
A new test case.
# Objective
When implementing `SystemParam` for an object which contains a mutable
reference to World, which cannot be derived due to a required lifetime
parameter, it's necessary to check that there aren't any conflicts.
As far as I know, the is_empty method is the only way provided to check
for no conflicts at all
# Objective
- Fixes https://github.com/bevyengine/bevy/issues/15389.
- Add documentation for RawHandleWrapper::_window field since It's
needed to drop the window at the correct time.
## Solution
- Added documentation to RawHandleWrapper::_window field as same as
WindowWrapper documentation.
## Testing
- No testing needed since it is documentation.
---
# Objective
Subtle, very minor issue. The following code fails to compile on main:
```rs
struct bevy;
#[derive(::bevy::ecs::component::Component)]
struct MyComponent;
```
The derive proc macro is pasting in essentially:
```rs
impl bevy::ecs::component::Component for MyComponent
```
...which normally works, but I've added `struct bevy`, which makes the
proc macro spit out incorrect code.
Very cursed, but to my knowledge has never been encountered in practice.
All the same, it's technically incorrect and should be fixed.
## Solution
The solution is simply to prepend `::` to crate names. Specifically, all
(all?) Bevy's derive macros determine the root crate name using
`BevyManifest`, which does some toml-parsing witchcraft to figure out
whether to qualify names using the umbrella `bevy` crate or individual
`bevy_xxx` crates. I just added a `::` to the spot where we parse the
`syn::Path`. The above example compiles properly after that.
## Testing
- CI should catch any errors since this change should cause compile
errors if for some reason they're being used in a cursed way somewhere
that would make this break something.
## Note
If this does break something for someone, this *really* needs a comment
in `BevyManifest::maybe_get_path` explaining why we can't make this
change.
# Objective
Fixes#18943
## Solution
Reintroduces support for `hashbrown`'s `HashMap` and `HashSet` types.
These were inadvertently removed when `bevy_platform` newtyped the
`hashbrown` types.
Since we removed our `hashbrown` dependency, I gated these impls behind
a `hashbrown` feature. Not entirely sure if this is necessary since we
enabled it for `bevy_reflect` through `bevy_platform` anyways. (Complex
features still confuse me a bit so let me know if I can just remove it!)
I also went ahead and preemptively implemented `TypePath` for `PassHash`
while I was here.
## Testing
You can test that it works by adding the following to a Bevy example
based on this PR (you'll also need to include `hashbrown` of course):
```rust
#[derive(Reflect)]
struct Foo(hashbrown::HashMap<String, String>);
```
Then check it compiles with:
```
cargo check --example hello_world --no-default-features --features=bevy_reflect/hashbrown
```
# Objective
- Fixes#18856.
## Solution
After PR #17633, `Camera::viewport_to_world` method corrects
`viewport_position` passed in that input so that it's offset by camera's
viewport. `Camera::viewport_to_world` is used by `make_ray` function
which in turn also offsets pointer position by viewport position, which
causes picking objects to be shifted by viewport position, and it wasn't
removed in the aforementioned PR. This second offsetting in `make_ray`
was removed.
## Testing
- I tested simple_picking example by applying some horizontal offset to
camera's viewport.
- I tested my application that displayed a single rectangle with picking
on two cameras arranged in a row. When using local bevy with this fix,
both cameras can be used for picking correctly.
- I modified split_screen example: I added observer to ground plane that
changes color on hover, and removed UI as it interfered with picking
both on master and my branch. On master, only top left camera was
triggering the observer, and on my branch all cameras could change
plane's color on hover.
- I added viewport offset to mesh_picking, with my changes it works
correctly, while on master picking ray is shifted.
- Sprite picking with viewport offset doesn't work both on master and on
this branch.
These are the only scenarios I tested. I think other picking functions
that use this function should be tested but I couldn't track more uses
of it.
Co-authored-by: Krzysztof Zywiecki <krzysiu@pop-os.Dlink>
# Objective
Fixes#18857.
## Solution
Add the requested method, and a `try_` variant as well.
## Testing
It compiles, doctests succeed, and is trivial enough that I don't think
it needs a unit test (correct me if I'm wrong though).
# Objective
Fix problems with `FontAtlasSet`:
* `FontAtlasSet` derives `Asset` but `FontAtlasSet`s are not Bevy
assets.
* The doc comments state that `FontAtlasSet`s are assets that are
created automatically when fonts are loaded. They aren't, they are
created as needed during text updates.
## Solution
* Removed the `Asset` derive.
* Rewrote the doc comments.
# Objective
Was copying off `bevy_ui`'s homework writing a picking backend and
noticed the `Has<IsDefaultPickingCamera>` is not used anywhere.
## Testing
Ran a random example.
This shouldn't cause any behavioral changes at all because the
component/archetype access/filter flags should be the same. `Has<X>`
doesn't affect access since it doesn't actually read or write anything,
and it doesn't affect matched archetypes either. Can't think of another
reason any behavior would change.
# Objective
A small typo was found on `bevy_ecs/examples/event.rs`.
I know it's very minor but I'd think fixing it would still help others
in the long run.
## Solution
Fix the typo.
## Testing
I don't think this is necessary.
# Objective
Fixes#18843
## Solution
We need to account for the material being added and removed in the
course of the same frame. We evict the caches first because the entity
will be re-added if it was marked as needing specialization, which
avoids another check on removed components to see if it was "really"
despawned.
# Objective
One to one relationships (added in
https://github.com/bevyengine/bevy/pull/18087) can currently easily be
invalidated by having two entities relate to the same target.
Alternative to #18817 (removing one-to-one relationships)
## Solution
Panic if a RelationshipTarget is already targeted. Thanks @urben1680 for
the idea!
---------
Co-authored-by: François Mockers <mockersf@gmail.com>
There's still a race resulting in blank materials whenever a material of
type A is added on the same frame that a material of type B is removed.
PR #18734 improved the situation, but ultimately didn't fix the race
because of two issues:
1. The `late_sweep_material_instances` system was never scheduled. This
PR fixes the problem by scheduling that system.
2. `early_sweep_material_instances` needs to be called after *every*
material type has been extracted, not just when the material of *that*
type has been extracted. The `chain()` added during the review process
in PR #18734 broke this logic. This PR reverts that and fixes the
ordering by introducing a new `SystemSet` that contains all material
extraction systems.
I also took the opportunity to switch a manual reference to
`AssetId::<StandardMaterial>::invalid()` to the new
`DUMMY_MESH_MATERIAL` constant for clarity.
Because this is a bug that can affect any application that switches
material types in a single frame, I think this should be uplifted to
Bevy 0.16.
This reverts commit ac52cca033.
Fixes#18815
# Objective
#18782 resulted in using `log` macros instead of `tracing` macros (in
the interest of providing no_std support, specifically no_atomic
support). That tradeoff isn't worth it, especially given that tracing is
likely to get no_atomic support.
## Solution
Revert #18782
Fixes#18834.
`EntityWorldMut::remove_children` and `EntityCommands::remove_children`
were removed in the relationships overhaul (#17398) and never got
replaced.
I don't *think* this was intentional (the methods were never mentioned
in the PR or its comments), but I could've missed something.
# Objective
I was wrong about how RPITIT works when I wrote this stuff initially,
and in order to actually give people access to all the traits
implemented by the output (e.g. Debug and so on) it's important to
expose the real output type, even if it makes the trait uglier and less
comprehensible. (☹️)
## Solution
Expose the curve output type of the `CurveWithDerivative` trait and its
double-derivative companion. I also added a bunch of trait derives to
`WithDerivative<T>`, since I think that was just an oversight.
Fixes a small mix-up from #18058, which added bulk relationship
replacement methods.
`EntityCommands::replace_related_with_difference` calls
`EntityWorldMut::replace_children_with_difference` instead of
`EntityWorldMut::replace_related_with_difference`, which means it always
operates on the `ChildOf` relationship instead of the `R: Relationship`
generic it's provided.
`EntityCommands::replace_children_with_difference` takes an `R:
Relationship` generic that it shouldn't, but it accidentally works
correctly on `main` because it calls the above method.
# Objective
After #17967, closures which always panic no longer satisfy various Bevy
traits. Principally, this affects observers, systems and commands.
While this may seem pointless (systems which always panic are kind of
useless), it is distinctly annoying when using the `todo!` macro, or
when writing tests that should panic.
Fixes#18778.
## Solution
- Add failing tests to demonstrate the problem
- Add the trick from
[`never_say_never`](https://docs.rs/never-say-never/latest/never_say_never/)
to name the `!` type on stable Rust
- Write looots of docs explaining what the heck is going on and why
we've done this terrible thing
## To do
Unfortunately I couldn't figure out how to avoid conflicting impls, and
I am out of time for today, the week and uh the week after that.
Vacation! If you feel like finishing this for me, please submit PRs to
my branch and I can review and press the button for it while I'm off.
Unless you're Cart, in which case you have write permissions to my
branch!
- [ ] fix for commands
- [ ] fix for systems
- [ ] fix for observers
- [ ] revert https://github.com/bevyengine/bevy-website/pull/2092/
## Testing
I've added a compile test for these failure cases and a few adjacent
non-failing cases (with explicit return types).
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
The parameter `In` of `call_inner` is completely unconstrained by its
arguments and return type. We are only able to infer it by assuming that
the only associated type equal to `In::Param<'_>` is `In::Param<'_>`
itself. It could just as well be some other associated type which only
normalizes to `In::Param<'_>`. This will change with the next-generation
trait solver and was encountered by a crater run
https://github.com/rust-lang/rust/pull/133502-
cc
https://github.com/rust-lang/trait-system-refactor-initiative/issues/168
I couldn't think of a cleaner alternative here. I first tried to just
provide `In` as an explicit type parameter. This is also kinda ugly as I
need to provide a variable number of them and `${ignore(..)}` is
currently still unstable https://github.com/rust-lang/rust/issues/83527.
Sorry for the inconvenience. Also fun that this function exists to avoid
a separate solver bug in the first place 😅
Fixes#18809Fixes#18823
Meshes despawned in `Last` can still be in visisible entities if they
were visible as of `PostUpdate`. Sanity check that the mesh actually
exists before we specialize. We still want to unconditionally assume
that the entity is in `EntitySpecializationTicks` as its absence from
that cache would likely suggest another bug.
# Objective
Fixes#18808
## Solution
When an asset emits a removed event, mark it as modified in the render
world to ensure any appropriate bookkeeping runs as necessary.
# Objective
The goal of `bevy_platform_support` is to provide a set of platform
agnostic APIs, alongside platform-specific functionality. This is a high
traffic crate (providing things like HashMap and Instant). Especially in
light of https://github.com/bevyengine/bevy/discussions/18799, it
deserves a friendlier / shorter name.
Given that it hasn't had a full release yet, getting this change in
before Bevy 0.16 makes sense.
## Solution
- Rename `bevy_platform_support` to `bevy_platform`.
# Objective
- `bevy_dylib` currently doesn't build independently
```
cargo build -p bevy_dylib
Compiling bevy_dylib v0.16.0-rc.4 (/crates/bevy_dylib)
error: no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait
error: `#[panic_handler]` function required, but not found
error: unwinding panics are not supported without std
|
= help: using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding
= note: since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem
error: could not compile `bevy_dylib` (lib) due to 3 previous errors
```
## Solution
- remove `#![no_std]` from `bevy_dylib`
## Testing
- it builds now
# Objective
- Fixes#18781
## Solution
- Moved `LogPlugin` into its own file gated behind a new `tracing`
feature.
- Used `log` instead of `tracing` where possible.
- Exposed a new `tracing` feature in `bevy` which enables
`bevy_log/tracing`.
- Gated `LogPlugin` from `DefaultPlugins` on `tracing` feature.
## Testing
- CI
---
## Migration Guide
- If you were previously using `bevy_log` with default features
disabled, enable the new `std` and `tracing` features.
- If you were using `bevy` with the default features disabled, enable
the new `tracing` feature.
## Notes
Almost all of the diffs in this PR come from moving `LogPlugin` into its
own file. This just makes the PR less noisy, since the alternative is
excessive `#[cfg(feature = "tracing")]` directives all over the plugin.
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
Fixes#17591
Looking at the arm downloads page, "r48p0" is a version number that
increments, where rXX is the major version and pX seems to be a patch
version. Take the conservative approach here that we know gpu
preprocessing is working on at least version 48 and presumably higher.
The assumption here is that the driver_info string will be reported
similarly on non-pixel devices.
# Objective
- Piped systems are an edge case that we missed when reworking system
parameter validation.
- Fixes#18755.
## Solution
- Validate the parameters for both systems, ~~combining the errors if
both failed validation~~ by simply using an early out.
- ~~Also fix the same bug for combinator systems while we're here.~~
## Testing
I've added a large number of tests checking the behavior under various
permutations. These are separate tests, rather than one mega test
because a) it's easier to track down bugs that way and b) many of these
are `should_panic` tests, which will halt the evaluation of the rest of
the test!
I've also added a test for exclusive systems being pipeable because we
don't have one and I was very surprised that that works!
---------
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
# Objective
- The referenced `ScheduleLabel` for `OnPrimaryClosed` and `OnAllClosed`
in `ExitCondition` was incorrect
## Solution
- Changed `Update` to `PostUpdate`
A clippy failure slipped into #18768, although I'm not sure why CI
didn't catch it.
```sh
> cargo clippy --version
clippy 0.1.85 (4eb161250e 2025-03-15)
> cargo run -p ci
...
error: empty line after doc comment
--> crates\bevy_pbr\src\light\mod.rs:105:5
|
105 | / /// The width and height of each of the 6 faces of the cubemap.
106 | |
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments
= note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]`
= help: if the empty line is unintentional remove it
help: if the documentation should include the empty line include it in the comment
|
106 | ///
|
```
# Objective
- Improve the docs for `PointLightShadowMap` and
`DirectionalLightShadowMap`
## Solution
- Add example for how to use `PointLightShadowMap` and move the
`DirectionalLightShadowMap` example from `DirectionalLight`.
- Match `PointLight` and `DirectionalLight` docs about shadows.
- Describe what `size` means.
---------
Co-authored-by: Robert Swain <robert.swain@gmail.com>
# Objective
Fixes#16896Fixes#17737
## Solution
Adds a new render phase, including all the new cold specialization
patterns, for wireframes. There's a *lot* of regrettable duplication
here between 3d/2d.
## Testing
All the examples.
## Migration Guide
- `WireframePlugin` must now be created with
`WireframePlugin::default()`.
Currently, `RenderMaterialInstances` and `RenderMeshMaterialIds` are
very similar render-world resources: the former maps main world meshes
to typed material asset IDs, and the latter maps main world meshes to
untyped material asset IDs. This is needlessly-complex and wasteful, so
this patch unifies the two in favor of a single untyped
`RenderMaterialInstances` resource.
This patch also fixes a subtle issue that could cause mesh materials to
be incorrect if a `MeshMaterial3d<A>` was removed and replaced with a
`MeshMaterial3d<B>` material in the same frame. The problematic pattern
looks like:
1. `extract_mesh_materials<B>` runs and, seeing the
`Changed<MeshMaterial3d<B>>` condition, adds an entry mapping the mesh
to the new material to the untyped `RenderMeshMaterialIds`.
2. `extract_mesh_materials<A>` runs and, seeing that the entity is
present in `RemovedComponents<MeshMaterial3d<A>>`, removes the entry
from `RenderMeshMaterialIds`.
3. The material slot is now empty, and the mesh will show up as whatever
material happens to be in slot 0 in the material data slab.
This commit fixes the issue by splitting out `extract_mesh_materials`
into *three* phases: *extraction*, *early sweeping*, and *late
sweeping*, which run in that order:
1. The *extraction* system, which runs for each material, updates
`RenderMaterialInstances` records whenever `MeshMaterial3d` components
change, and updates a change tick so that the following system will know
not to remove it.
2. The *early sweeping* system, which runs for each material, processes
entities present in `RemovedComponents<MeshMaterial3d>` and removes each
such entity's record from `RenderMeshInstances` only if the extraction
system didn't update it this frame. This system runs after *all*
extraction systems have completed, fixing the race condition.
3. The *late sweeping* system, which runs only once regardless of the
number of materials in the scene, processes entities present in
`RemovedComponents<ViewVisibility>` and, as in the early sweeping phase,
removes each such entity's record from `RenderMeshInstances` only if the
extraction system didn't update it this frame. At the end, the late
sweeping system updates the change tick.
Because this pattern happens relatively frequently, I think this PR
should land for 0.16.
Due to the preprocessor usage in the shader, different combinations of
features could cause the fields of `StandardMaterialBindings` to shift
around. In certain cases, this could cause them to not line up with the
bindings specified in `StandardMaterial`. This resulted in #18104.
This commit fixes the issue by making `StandardMaterialBindings` have a
fixed size. On the CPU side, it uses the
`#[bindless(index_table(range(M..N)))]` feature I added to `AsBindGroup`
in #18025 to do so. Thus this patch has a dependency on #18025.
Closes#18104.
---------
Co-authored-by: Robert Swain <robert.swain@gmail.com>
This fixes a panic that occurs if one calls
`PipelineCache::get_render_pipeline_state(id)` or
`PipelineCache::get_compute_pipeline_state(id)` with a queued pipeline
id that has not yet been processed by `PipelineCache::process_queue()`.
```
thread 'Compute Task Pool (0)' panicked at [...]/bevy/crates/bevy_render/src/render_resource/pipeline_cache.rs:611:24:
index out of bounds: the len is 0 but the index is 20
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
# Objective
Fixes#18550.
Because bin state for unbatchable meshes wasn't being cleared each
frame, the buffer indices for unbatchable meshes would demote from
sparse to dense storage and aggressively leak memory, with all kinds of
weird consequences downstream, namely supplying invalid instance ranges
for render.
## Solution
Clear out the unbatchable mesh bin state when we start a new frame.
PR #17898 disabled bindless support for `ExtendedMaterial`. This commit
adds it back. It also adds a new example, `extended_material_bindless`,
showing how to use it.
# Objective
Fixes#18678
## Solution
Moved the current `with_related` method to `with_relationships` and
added a new `with_related` that uses a bundle.
I'm not entirely sold on the name just yet, if anyone has any ideas let
me know.
## Testing
I wasn't able to test these changes because it crashed my computer every
time I tried (fun). But there don't seem to be any tests that use the
old `with_related` method so it should be fine, hopefully
## Showcase
```rust
commands.spawn_empty()
.with_related::<Relationship>(Name::new("Related thingy"))
.with_relationships(|rel| {
rel.spawn(Name::new("Second related thingy"));
});
```
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
- Allow viewing and setting the added tick for change detection aware
data, to allow operations like checking if the value has been modified
since first being added, and spoofing that state (i.e. returning the
value to default in place without a remove/insert dance)
## Solution
- Added corresponding functions matching the existing `changed` API:
- `fn added(&self) -> Tick`
- `fn set_added(&mut self)`
- `fn set_last_added(&mut self, last_added: Tick)`
Discussed on discord @
https://canary.discord.com/channels/691052431525675048/749335865876021248/1358718892465193060
## Testing
- Running the bevy test suite by.. making a PR, heck.
- No new tests were introduced due to triviality (i.e. I don't know what
to test about this API, and the corresponding API for `changed` is
similarly lacking tests.)
---------
Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net>
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>