# Objective
`Populated`, a loose wrapper around `Query`, does not implement
`IntoIterator`, requiring either a deref or `into_inner()` call to
access the `Query` and iterate over that.
## Solution
This pr implements `IntoIterator` for `Populated`, `&Populated`, and
`&mut Populated`, each of which forwards the call to the inner `Query`.
This allows the `Populated` to be used directly for any API that takes
an `impl IntoIterator`.
## Testing
`cargo test` was run on the `bevy_ecs` crate
```
test result: ok. 390 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 46.38s
```
# Objective
Fix https://github.com/bevyengine/bevy/issues/18558
## Solution
* Replace `T` in docs with `Self`
* Fix broken link - replace "introspection subtraits" with "reflection
subtraits"
* Added missing `Set` variant to the list of per-`ReflectKind`-variation
behaviours
## Testing
cargo doc --serve locally to check that the broken link is fixed
---------
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
The first 4 commits are designed to be reviewed independently.
- Mark TAA non-experimental now that motion vectors are written for
skinned and morphed meshes, along with skyboxes, and add it to
DefaultPlugins
- Adjust halton sequence to match what DLSS is going to use, doesn't
really affect anything, but may as well
- Make MipBias a required component on TAA instead of inserting it in
the render world
- Remove MipBias, TemporalJitter, RenderLayers, etc from the render
world if they're removed from the main world (fixes a retained render
world bug)
- Remove TAA components from the render world properly if
TemporalAntiAliasing is removed from the main world (fixes a retained
render world bug)
- extract_taa_settings() now has to query over `Option<&mut
TemporalAntiAliasing>`, which will match every single camera, in order
to cover cameras that had TemporalAntiAliasing removed this frame. This
kind of sucks, but I can't think of anything better.
- We probably have the same bug with every other rendering feature
component we have.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Ultimately, I'd like to modify our font atlas creation systems so that
they are able to resize the font atlases as more glyphs are added. At
the moment, they create a new 512x512 atlas every time one fills up.
With large font sizes and many glyphs, your glyphs may end up spread out
across several atlases.
The goal would be to render text more efficiently, because glyphs spread
across fewer textures could benefit more from batching.
`AtlasAllocator` already has support for growing atlases, but we don't
currently have a way of growing a texture while keeping the pixel data
intact.
## Solution
Add a new method to `Image`: `resize_in_place` and a test for it.
## Testing
Ran the new test, and also a little demo comparing this side-by-side
with `resize`.
<details>
<summary>Expand Code</summary>
```rust
//! Testing ground for #19410
use bevy::prelude::*;
use bevy_render::render_resource::Extent3d;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, test)
.init_resource::<Size>()
.insert_resource(FillColor(Hsla::hsl(0.0, 1.0, 0.7)))
.run();
}
#[derive(Resource, Default)]
struct Size(Option<UVec2>);
#[derive(Resource)]
struct FillColor(Hsla);
#[derive(Component)]
struct InPlace;
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
commands.spawn((
Transform::from_xyz(220.0, 0.0, 0.0),
Sprite::from_image(asset_server.load("branding/bevy_bird_dark.png")),
));
commands.spawn((
InPlace,
Transform::from_xyz(-220.0, 0.0, 0.0),
Sprite::from_image(asset_server.load("branding/icon.png")),
));
}
fn test(
sprites: Query<(&Sprite, Has<InPlace>)>,
mut images: ResMut<Assets<Image>>,
mut new_size: ResMut<Size>,
mut dir: Local<IVec2>,
mut color: ResMut<FillColor>,
) -> Result {
for (sprite, in_place) in &sprites {
let image = images.get_mut(&sprite.image).ok_or("Image not found")?;
let size = new_size.0.get_or_insert(image.size());
if *dir == IVec2::ZERO {
*dir = IVec2::splat(1);
}
*size = size.saturating_add_signed(*dir);
if size.x > 400 || size.x < 150 {
*dir = *dir * -1;
}
color.0 = color.0.rotate_hue(1.0);
if in_place {
image.resize_in_place_2d(
Extent3d {
width: size.x,
height: size.y,
..default()
},
&Srgba::from(color.0).to_u8_array(),
)?;
} else {
image.resize(Extent3d {
width: size.x,
height: size.y,
..default()
});
}
}
Ok(())
}
```
</details>
https://github.com/user-attachments/assets/6b2d0ec3-6a6e-4da1-98aa-29e7162f16fa
## Alternatives
I think that this might be useful functionality outside of the font
atlas scenario, but we *could* just increase the initial font atlas
size, make it configurable, and/or size font atlases according to device
limits. It's not totally clear to me how to accomplish that last idea.
This adds support for clearing events when **entering** a state (instead
of just when exiting) and updates the names to match
`DespawnOnExitState`.
Before:
```rust
app.add_state_scoped_event::<MyGameEvent>(GameState::Play);
```
After:
```rust
app
.add_event::<MyGameEvent>()
.clear_events_on_exit_state::<MyGameEvent>(GameState::Play);
```
# Objective
This is the first step of #19430 and is a follow up for #19132.
Now that `ArchetypeRow` has a niche, we can use `Option` instead of
needing `INVALID` everywhere.
This was especially concerning since `INVALID` *really was valid!*
Using options here made the code clearer and more data-driven.
## Solution
Replace all uses of `INVALID` entity locations (and archetype/table
rows) with `None`.
## Testing
CI
---------
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Fixes#19029 (also maybe sorta #18002, but we may want to handle the SPA
issue I outlined there more gracefully?)
## Solution
The most minimal / surgical approach I could think of, hopefully
cherry-pickable for a point release.
It seems that it's not *entirely* crazy for web services to return 403
for an item that was not found. Here's an example from [Amazon
CloudFront
docs](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/http-403-permission-denied.html#s3-origin-403-error).
If it is somewhat common for web services to behave this way, then I
think it's best to also treat these responses as if they were "not
found."
I was previously of the opinion that any 400 level error "might as well"
get this treatment, but I'm now thinking that's probably overkill and
there are quite a few 400 level statuses that would indicate some
problem that needs to be fixed, and interpreting these as "not found"
might add confusion to the debugging process.
## Testing
Tested this with a web server that returns 403 for requests to meta
files.
```bash
cargo run -p build-wasm-example -- --api webgl2 sprite && \
open "http://localhost:4000" && \
python3 test_403.py examples/wasm
```
`test_403.py`:
```python
from http.server import HTTPServer, SimpleHTTPRequestHandler
import os
import sys
class CustomHandler(SimpleHTTPRequestHandler):
def do_GET(self):
if self.path.endswith(".meta"):
self.send_response(403)
self.send_header("Content-type", "text/plain")
self.end_headers()
self.wfile.write(b"403 Forbidden: Testing.\n")
else:
super().do_GET()
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <directory>")
sys.exit(1)
os.chdir(sys.argv[1])
server_address = ("", 4000)
httpd = HTTPServer(server_address, CustomHandler)
httpd.serve_forever()
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Fixed#19035. Fixed#18882. It consisted of two different bugs:
- The allocations where being incremented even when a Data binding was
created.
- The ref counting on the binding was broken.
## Solution
- Stopped incrementing the allocations when a data binding was created.
- Rewrote the ref counting code to more reliably track the ref count.
## Testing
Tested my fix for 10 minutes with the `examples/3d/animated_material.rs`
example. I changed the example to spawn 51x51 meshes instead of 3x3
meshes to heighten the effects of the bug.
My branch: (After 10 minutes of running the modified example)
GPU: 172 MB
CPU: ~700 MB
Main branch: (After 2 minutes of running the modified example, my
computer started to stutter so I had to end it early)
GPU: 376 MB
CPU: ~1300 MB
# 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
- Due to recent changes related to #19024, the
`compute_shader_game_of_life` example panics on some machines especially
on Linux.
- This is due to us switching more shaders to embedded shaders - this
means the compute shader in this example takes more than one frame to
load.
- The panic in the example occurs if the shader fails to load by the
first frame (since the pipeline considers that an error).
## Solution
- Make the example do nothing if the shader isn't loaded yet. This has
the effect of waiting for the shader to load.
## Testing
- Tested the example on my Linux laptop.
# 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
Minimal effort to address feedback here:
https://github.com/bevyengine/bevy/pull/19345#discussion_r2107844018
more thoroughly.
## Solution
- Remove hardcoded label string comparisons and make more use of the new
enum added during review
- Resist temptation to let this snowball this into a huge refactor
- Maybe come back later for a few other small improvements
## Testing
`cargo run --example box_shadow`
# 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#19385
Note: this has shader errors due to #19383 and should probably be merged
after #19384
## Solution
- Move the example to the UI testbed
- Adjust label contents and cell size so that every test case fits on
the screen
- Minor tidying, slightly less harsh colors while preserving the
intentional debug coloring
## Testing
`cargo run --example testbed_ui`

---------
Co-authored-by: François Mockers <mockersf@gmail.com>
# 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
- Addresses the previous example's lack of visual appeal and clarity. It
was missing labels for clear distinction of the shadow settings used on
each of the shapes. The suggestion in the linked issue was to either
just visually update and add labels or to collapse example to a single
node with adjustable settings.
- Fixes#19240
## Solution
- Replace the previous static example with a single, central node with
adjustable settings as per issue suggestion.
- Implement button-based setting adjustments. Unfortunately slider
widgets don't seem available yet and I didn't want to further bloat the
example.
- Improve overall aesthetics of the example -- although color pallette
could still be improved. flat gray tones are probably not the best
choice as a contrast to the shadow, but the white border does help in
that aspect.
- Dynamically recolor shadows for visual clarity when increasing shadow
count.
- Add Adjustable Settings:
- Shape selection
- Shadow X/Y offset, blur, spread, and count
- Add Reset button to restore default settings
The disadvantage of this solution is that the old example code would
have probably been easier to digest as the new example is quite bloated
in comparison. Alternatively I could also just implement labels and fix
aesthetics of the old example without adding functionality for
adjustable settings, _but_ I personally feel like interactive examples
are more engaging to users.
## Testing
- Did you test these changes? If so, how? `cargo run --example
box_shadow` and functionality of all features of the example.
- Are there any parts that need more testing? Not that I am aware of.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know? Not really, it should be pretty
straightforward just running the new example and testing the feats.
---
## Showcase


---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
# 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.