This PR adds support for *mixed lighting* to Bevy, whereby some parts of
the scene are lightmapped, while others take part in real-time lighting.
(Here *real-time lighting* means lighting at runtime via the PBR shader,
as opposed to precomputed light using lightmaps.) It does so by adding a
new field, `affects_lightmapped_meshes` to `IrradianceVolume` and
`AmbientLight`, and a corresponding field
`affects_lightmapped_mesh_diffuse` to `DirectionalLight`, `PointLight`,
`SpotLight`, and `EnvironmentMapLight`. By default, this value is set to
true; when set to false, the light contributes nothing to the diffuse
irradiance component to meshes with lightmaps.
Note that specular light is unaffected. This is because the correct way
to bake specular lighting is *directional lightmaps*, which we have no
support for yet.
There are two general ways I expect this field to be used:
1. When diffuse indirect light is baked into lightmaps, irradiance
volumes and reflection probes shouldn't contribute any diffuse light to
the static geometry that has a lightmap. That's because the baking tool
should have already accounted for it, and in a higher-quality fashion,
as lightmaps typically offer a higher effective texture resolution than
the light probe does.
2. When direct diffuse light is baked into a lightmap, punctual lights
shouldn't contribute any diffuse light to static geometry with a
lightmap, to avoid double-counting. It may seem odd to bake *direct*
light into a lightmap, as opposed to indirect light. But there is a use
case: in a scene with many lights, avoiding light leaks requires shadow
mapping, which quickly becomes prohibitive when many lights are
involved. Baking lightmaps allows light leaks to be eliminated on static
geometry.
A new example, `mixed_lighting`, has been added. It demonstrates a sofa
(model from the [glTF Sample Assets]) that has been lightmapped offline
using [Bakery]. It has four modes:
1. In *baked* mode, all objects are locked in place, and all the diffuse
direct and indirect light has been calculated ahead of time. Note that
the bottom of the sphere has a red tint from the sofa, illustrating that
the baking tool captured indirect light for it.
2. In *mixed direct* mode, lightmaps capturing diffuse direct and
indirect light have been pre-calculated for the static objects, but the
dynamic sphere has real-time lighting. Note that, because the diffuse
lighting has been entirely pre-calculated for the scenery, the dynamic
sphere casts no shadow. In a real app, you would typically use real-time
lighting for the most important light so that dynamic objects can shadow
the scenery and relegate baked lighting to the less important lights for
which shadows aren't as important. Also note that there is no red tint
on the sphere, because there is no global illumination applied to it. In
an actual game, you could fix this problem by supplementing the
lightmapped objects with an irradiance volume.
3. In *mixed indirect* mode, all direct light is calculated in
real-time, and the static objects have pre-calculated indirect lighting.
This corresponds to the mode that most applications are expected to use.
Because direct light on the scenery is computed dynamically, shadows are
fully supported. As in mixed direct mode, there is no global
illumination on the sphere; in a real application, irradiance volumes
could be used to supplement the lightmaps.
4. In *real-time* mode, no lightmaps are used at all, and all punctual
lights are rendered in real-time. No global illumination exists.
In the example, you can click around to move the sphere, unless you're
in baked mode, in which case the sphere must be locked in place to be
lit correctly.
## Showcase
Baked mode:

Mixed direct mode:

Mixed indirect mode (default):

Real-time mode:

## Migration guide
* The `AmbientLight` resource, the `IrradianceVolume` component, and the
`EnvironmentMapLight` component now have `affects_lightmapped_meshes`
fields. If you don't need to use that field (for example, if you aren't
using lightmaps), you can safely set the field to true.
* `DirectionalLight`, `PointLight`, and `SpotLight` now have
`affects_lightmapped_mesh_diffuse` fields. If you don't need to use that
field (for example, if you aren't using lightmaps), you can safely set
the field to true.
[glTF Sample Assets]:
https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main
[Bakery]:
https://geom.io/bakery/wiki/index.php?title=Bakery_-_GPU_Lightmapper
# Objective
- Wgpu barrier tracking is expensive. Making buffers read-only makes
ideally lets wgpu skip worrying about barriers, although in wgpu 23 it
apparently won't yet.
## Solution
- Remove COPY_DST usage from AsBindGroup uniform buffers to allow future
wgpu versions to make this cheaper.
- AsBindGroup never updates buffers, so there's no need for COPY_DST. We
always recreate all buffers and the bind group every time data changes,
which yeah is also expensive.
## Testing
- Ran the animated materials example with/without bindless enabled. No
crashes.
This commit allows Bevy to bind 16 lightmaps at a time, if the current
platform supports bindless textures. Naturally, if bindless textures
aren't supported, Bevy falls back to binding only a single lightmap at a
time. As lightmaps are usually heavily atlased, I doubt many scenes will
use more than 16 lightmap textures.
This has little performance impact now, but it's desirable for us to
reap the benefits of multidraw and bindless textures on scenes that use
lightmaps. Otherwise, we might have to break batches in order to switch
those lightmaps.
Additionally, this PR slightly reduces the cost of binning because it
makes the lightmap index in `Opaque3dBinKey` 32 bits instead of an
`AssetId`.
## Migration Guide
* The `Opaque3dBinKey::lightmap_image` field is now
`Opaque3dBinKey::lightmap_slab`, which is a lightweight identifier for
an entire binding array of lightmaps.
# Objective
Scroll position uses physical coordinates. This means scrolling may go
faster or slower depending on the scroll factor. Also the scrolled
position will change when the scale factor changes.
## Solution
In `ui_layout_system` convert `max_possible_offset` to logical
coordinates before clamping the scroll position. Then convert the
clamped scroll position to physical coordinates before propagating it to
the node's children.
## Testing
Look at the `scroll` example. On main if you change your display's scale
factor the items displayed by the scrolling lists will change because
`ScrollPosition`'s displacement values don't respect scale factor. With
this PR the displacement will be scaled too, and the won't move.
# Objective
`PartialReflect::serializable` is unused in the codebase and should be
removed.
I believe it originally was used to handle serializing certain types but
that's no longer the case.
## Solution
Remove `PartialReflect::serializable`.
## Testing
You can check locally using:
```
cargo check -p bevy_reflect --all-features
```
---
## Migration Guide
`PartialReflect::serializable` has been removed. If you were using this
to pass on serialization information, use `ReflectSerialize` instead or
create custom type data to generate the `Serializable`.
# Objective
We were waiting for 1.83 to address most of these, due to a bug with
`missing_docs` and `expect`. Relates to, but does not entirely complete,
#15059.
## Solution
- Upgrade to 1.83
- Switch `allow(missing_docs)` to `expect(missing_docs)`
- Remove a few now-unused `allow`s along the way, or convert to `expect`
## Objective
Thanks to @eugineerd's work on entity cloning (#16132), we now have a
robust way to copy components between entities. We can extend this to
implement some useful functionality that would have been more
complicated before.
Closes#15350.
## Solution
`EntityCloneBuilder` now automatically includes required components
alongside any component added/removed from the component filter.
Added the following methods to `EntityCloneBuilder`:
- `move_components`
- `without_required_components`
Added the following methods to `EntityWorldMut` and `EntityCommands`:
- `clone_with`
- `clone_components`
- `move_components`
Also added `clone_and_spawn` and `clone_and_spawn_with` to
`EntityWorldMut` (`EntityCommands` already had them).
## Showcase
```
assert_eq!(world.entity(entity_a).get::<B>(), Some(&B));
assert_eq!(world.entity(entity_b).get::<B>(), None);
world.entity_mut(entity_a).clone_components::<B>(entity_b);
assert_eq!(world.entity(entity_a).get::<B>(), Some(&B));
assert_eq!(world.entity(entity_b).get::<B>(), Some(&B));
assert_eq!(world.entity(entity_a).get::<C>(), Some(&C(5)));
assert_eq!(world.entity(entity_b).get::<C>(), None);
world.entity_mut(entity_a).move_components::<C>(entity_b);
assert_eq!(world.entity(entity_a).get::<C>(), None);
assert_eq!(world.entity(entity_b).get::<C>(), Some(&C(5)));
```
# Objective
- Prework for reviving #9582.
## Solution
- Move the two types to volume.rs and made it compile.
- Also `#[reflect(Debug)]` on `Volume` while I'm here.
## Testing
- Ran example locally.
- Rely on CI.
# Objective
- #16813 added the ability to mute sinks and added a new method
`toggle_mute()`.
- Leaving `toggle()` as is creates inconsistency and a bit of confusion
about what is being toggled.
## Solution
- Rename `toggle()` to `toggle_playback()`.
- The choice to use the `_playback` suffix was easy because the method
comment was already telling us what is being toggled: `Toggles playback
of the sink.`
- [Raised in Discord] and got the OK from Alice.
[Raised in Discord]:
https://discord.com/channels/691052431525675048/749430447326625812/1318000355824504905
## Testing
- I ran the example and also updated the instruction text to make it
clear `Space` is toggling the playback not just pausing.
- I added a unit test for `toggle_playback()` because why not.
---
## Showcase
Example instructions:
<img width="292" alt="image"
src="https://github.com/user-attachments/assets/585c36c6-c4d7-428b-acbe-a92f3a37b460"
/>
## Migration Guide
- `AudioSinkPlayback`'s `toggle` method has been renamed to
`toggle_playback`. This was done to create consistency with the
`toggle_mute` method added in
https://github.com/bevyengine/bevy/pull/16813. Change instances of
`toggle` to `toggle_playback`. E.g.:
Before:
```rust
fn pause(keyboard_input: Res<ButtonInput<KeyCode>>, sink: Single<&AudioSink>) {
if keyboard_input.just_pressed(KeyCode::Space) {
sink.toggle();
}
}
```
After:
```rust
fn pause(keyboard_input: Res<ButtonInput<KeyCode>>, sink: Single<&AudioSink>) {
if keyboard_input.just_pressed(KeyCode::Space) {
sink.toggle_playback();
}
}
```
# Objective
Fixes#16659
## Solution
- I just added all the `#[reflect(Component)]` attributes where
necessary.
## Testing
I wrote a small program that scans the bevy code for all structs and
enums that derive `Component` and `Reflect`, but don't have the
attribute `#[reflect(Component)]`.
I don't know if this testing program should be part of the testing suite
of bevy. It takes a bit of time to scan the whole codebase. In any case,
I've published it [here](https://github.com/anlumo/bevy-reflect-check).
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- Allow users to mute audio.
```rust
fn mute(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut sink: Single<&mut AudioSink, With<MyMusic>>,
) {
if keyboard_input.just_pressed(KeyCode::KeyM) {
sink.toggle_mute();
}
}
```
- I want to be able to press, say, `M` and mute all my audio. I want
this for dev, but I'm sure it's a useful player setting as well.
- Muting is different to pausing—I don't want to pause my sounds, I want
them to keep playing but with no volume. For example if I have
background music playing which is made up of 5 tracks, I want to be able
to temporarily mute my background music, and if I unmute at, say, track
4, I want to play track 4 rather than have had everything paused and
still be on the first track.
- I want to be able to continue to control the volume of my audio even
when muted. Like in the example, if I have muted my audio but I use the
volume up/down controls, I want Bevy to remember those volume changes so
that when I unmute, the volume corresponds to that.
## Solution
- Add methods to audio to allow muting, unmuting and toggling muting.
- To preserve the user's intended volume, each sink needs to keep track
of a "managed volume".
- I checked `rodio` and I don't see any built in support for doing this,
so I added it to `bevy_audio`.
- I'm interested to hear if this is a good idea or a bad idea. To me,
this API looks nice and looks usable, but I'm aware it involves some
changes to the existing API and now also requires mutable access in some
places compared to before.
- I'm also aware of work on *Better Audio*, but I'm hoping that if this
change isn't too wild it might be a useful addition considering we don't
really know when we'll eventually get better audio.
## Testing
- Update and run the example: `cargo run --example audio_control`
- Run the example: `cargo run --example soundtrack`
- Update and run the example: `cargo run --example spatial_audio_3d`
- Add unit tests.
---
## Showcase
See 2 changed examples that show how you can mute an audio sink and a
spatial audio sink.
## Migration Guide
- The `AudioSinkPlayback` trait now has 4 new methods to allow you to
mute audio sinks: `is_muted`, `mute`, `unmute` and `toggle_mute`. You
can use these methods on `bevy_audio`'s `AudioSink` and
`SpatialAudioSink` components to manage the sink's mute state.
- `AudioSinkPlayback`'s `set_volume` method now takes a mutable
reference instead of an immutable one. Update your code which calls
`set_volume` on `AudioSink` and `SpatialAudioSink` components to take a
mutable reference. E.g.:
Before:
```rust
fn increase_volume(sink: Single<&AudioSink>) {
sink.set_volume(sink.volume() + 0.1);
}
```
After:
```rust
fn increase_volume(mut sink: Single<&mut AudioSink>) {
let current_volume = sink.volume();
sink.set_volume(current_volume + 0.1);
}
```
- The `PlaybackSettings` component now has a `muted` field which you can
use to spawn your audio in a muted state. `PlaybackSettings` also now
has a helper method `muted` which you can use when building the
component. E.g.:
```rust
commands.spawn((
// ...
AudioPlayer::new(asset_server.load("sounds/Windless Slopes.ogg")),
PlaybackSettings::LOOP.with_spatial(true).muted(),
));
```
---------
Co-authored-by: Nathan Graule <solarliner@gmail.com>
I forgot to set `BINDLESS_SLOT_COUNT` in `ExtendedMaterial`'s
implementation of `AsBindGroup`, so it didn't actually become bindless.
In fact, it would usually crash with a shader/bind group layout
mismatch, because some parts of Bevy's renderer thought that the
resulting material was bindless while other parts didn't. This commit
corrects the situation.
I had to make `BINDLESS_SLOT_COUNT` a function instead of a constant
because the `ExtendedMaterial` version needs some logic. Unfortunately,
trait methods can't be `const fn`s, so it has to be a runtime function.
# Objective
Destructuring in const code blocks isn't allowed, thus using UIRect in
const code can be a hassle as it initialisation function aren't const.
This Pr makes them const.
## Solution
Removed all destructuring in the UIRect implementation
## Testing
- I've ran a few ui examples to check if i didn't make a mistake,
---
# Objective
- Minor consistency improvement in proc macro code.
- Remove `get_path_direct` since it was only used once anyways and
doesn't add much.
## Solution
- Possibly a minor performance improvement since the `Cargo.toml` wont
be parsed as often.
## Testing
- I don't think it breaks anything.
- This is my first time working on bevy itself. Is there a script to do
a quick verify of my pr?
## Other PR
Similar to #7536 but has no extra dependencies.
Co-authored-by: François Mockers <mockersf@gmail.com>
# Objective
- Describe the objective or issue this PR addresses.
- If you're fixing a specific issue, say "Fixes #X".
## Solution
- Describe the solution used to achieve the objective above.
## Testing
- Did you test these changes? If so, how?
- Are there any parts that need more testing?
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
---
## Showcase
> This section is optional. If this PR does not include a visual change
or does not add a new feature, you can delete this section.
- Help others understand the result of this PR by showcasing your
awesome work!
- If this PR adds a new feature or public API, consider adding a brief
pseudo-code snippet of it in action
- If this PR includes a visual change, consider adding a screenshot,
GIF, or video
- If you want, you could even include a before/after comparison!
- If the Migration Guide adequately covers the changes, you can delete
this section
While a showcase should aim to be brief and digestible, you can use a
toggleable section to save space on longer showcases:
<details>
<summary>Click to view showcase</summary>
```rust
println!("My super cool code.");
```
</details>
## Migration Guide
> This section is optional. If there are no breaking changes, you can
delete this section.
- If this PR is a breaking change (relative to the last release of
Bevy), describe how a user might need to migrate their code to support
these changes
- Simply adding new functionality is not a breaking change.
- Fixing behavior that was definitely a bug, rather than a questionable
design choice is not a breaking change.
# Objective
- Writing an API, and I want to allow users to pass in extra data
alongside the API provided input, and tuples are the most natural
extension in this case.
- Bring `SystemInput` up to par with `SystemParam` for tuple support.
## Solution
- Added impls for tuples up to 8 elements. If you need a 9-arity tuple
or more, write your own `SystemInput` type (it's incredibly simple to
do).
## Testing
- Added a test demonstrating this.
---
## Showcase
Tuples of arbitrary`SystemInput`s are now supported:
```rust
fn by_value((In(a), In(b)): (In<usize>, In<usize>)) -> usize {
a + b
}
fn by_mut((InMut(a), In(b)): (InMut<usize>, In<usize>)) {
*a += b;
}
let mut world = World::new();
let mut by_value = IntoSystem::into_system(by_value);
let mut by_mut = IntoSystem::into_system(by_mut);
by_value.initialize(&mut world);
by_mut.initialize(&mut world);
assert_eq!(by_value.run((12, 24), &mut world), 36);
let mut a = 10;
let b = 5;
by_mut.run((&mut a, b), &mut world);
assert_eq!(*a, 15);
```
# Objective
- Closes#16804.
- This copies over our lint configuration to our benchmarks and fixes
any lints.
## Solution
- Copied over our Clippy configuration from the root `Cargo.toml` to
`benches/Cargo.toml`.
- Fixed any warnings that Clippy emitted.
## Testing
- `cd benches && cargo clippy --benches`
# Objective
- Allow skiping components that don't have ComponentId yet instead of
failing `bevy/query` request.
## Solution
- Describe the solution used to achieve the objective above.
## Testing
My naive approach boils down to:
- bevy/list to get list of all components.
- bevy/query with empty components and has fields and a option that
contains result of the bevy/list.
Before that change I end up with bunch of `Component xxx isn't used in
the world` because some of the components wasn't spawned at any moment
yet in the game. Now it should work.
## Migration Guide
- `BrpQueryParams` now has `strict` boolean field. It serfs as a flag to
fail when encountering an invalid component rather than skipping it.
Defaults to false.
# Objective
Fixes#15485.
## Solution
Deletes the field! The `meta` field had no way to access or mutate it.
## Testing
- It builds!
---
## Migration Guide
- `ErasedAssetLoader` now takes a borrow to `AssetMetaDyn` instead of a
`Box`.
- `LoadedAsset::new_with_dependencies` no longer requires a `meta`
argument.
- `LoadContext::finish` no longer requires a `meta` argument.
# Objective
- `PointerInteraction` components should be updated before sending
picking events. Otherwise they will be stale when event observers run.
- Allow inserting logic before picking events but after
`PointerInteraction` components have been updated.
## Solution
- Reorder systems in `PickSet::Focus`.
This patch replaces the undocumented `NoGpuCulling` component with a new
component, `NoIndirectDrawing`, effectively turning indirect drawing on
by default. Indirect mode is needed for the recently-landed multidraw
feature (#16427). Since multidraw is such a win for performance, when
that feature is supported the small performance tax that indirect mode
incurs is virtually always worth paying.
To ensure that custom drawing code such as that in the
`custom_shader_instancing` example continues to function, this commit
additionally makes GPU culling take the `NoFrustumCulling` component
into account.
This PR is an alternative to #16670 that doesn't break the
`custom_shader_instancing` example. **PR #16755 should land first in
order to avoid breaking deferred rendering, as multidraw currently
breaks it**.
## Migration Guide
* Indirect drawing (GPU culling) is now enabled by default, so the
`GpuCulling` component is no longer available. To disable indirect mode,
which may be useful with custom render nodes, add the new
`NoIndirectDrawing` component to your camera.
## Objective
Some structs and methods in the ECS internals have names that don't
describe their purpose very well, and sometimes don't have docs either.
Also, the function `remove_bundle_from_archetype` is a counterpart to
`BundleInfo::add_bundle_to_archetype`, but isn't a method and is in a
different file.
## Solution
- Renamed the following structs and added docs:
| Before | After |
|----------------------|------------------------------|
| `AddBundle` | `ArchetypeAfterBundleInsert` |
| `InsertBundleResult` | `ArchetypeMoveType` |
- Renamed the following methods:
| Before | After |
|---------------------------------------|----------------------------------------------|
| `Edges::get_add_bundle` | `Edges::get_archetype_after_bundle_insert` |
| `Edges::insert_add_bundle` |
`Edges::cache_archetype_after_bundle_insert` |
| `Edges::get_remove_bundle` |
`Edges::get_archetype_after_bundle_remove` |
| `Edges::insert_remove_bundle` |
`Edges::cache_archetype_after_bundle_remove` |
| `Edges::get_take_bundle` | `Edges::get_archetype_after_bundle_take` |
| `Edges::insert_take_bundle` |
`Edges::cache_archetype_after_bundle_take` |
- Moved `remove_bundle_from_archetype` from `world/entity_ref.rs` to
`BundleInfo`. I left the function in entity_ref in the first commit for
comparison, look there for the diff of comments and whatnot.
- Tidied up docs:
- General grammar and spacing.
- Made the usage of "insert" and "add" more consistent.
- Removed references to information that isn't there.
- Renamed `BundleInfo::add_bundle_to_archetype` to
`BundleInfo::insert_bundle_into_archetype` for consistency.
# Objective
I was curious to use the newly created `bevy_input_focus`, but I found
some issues with it
- It was only implementing traits for `World`.
- Lack of tests
- `is_focus_within` logic was incorrect.
## Solution
This PR includes some improvements to the `bevy_input_focus` crate:
- Add new `IsFocusedHelper` that doesn't require access to `&World`. It
implements `IsFocused`
- Remove `IsFocused` impl for `DeferredWorld`. Since it already
implements `Deref<Target=World>` it was just duplication of code.
- impl `SetInputFocus` for `Commands`. There was no way to use
`SetFocusCommand` directly. This allows it.
- The `is_focus_within` logic has been fixed to check descendants.
Previously it was checking if any of the ancestors had focus which is
not correct according to the documentation.
- Added a bunch of unit tests to verify the logic of the crate.
## Testing
- Did you test these changes? If so, how? Yes, running newly added unit
tests.
---
# Objective
Fixes#16776
## Solution
- reflect `&'static Location` as an opaque type
- I've added this to `impls/std.rs` because other core types are there
too. Maybe they should be split out into a `core.rs` in another PR.
- add source location to `EventId` (behind the
`tracking_change_detection` feature flag)
## Testing
---
## Showcase
```rust
fn apply_damage_to_health(
mut dmg_events: EventReader<DealDamage>,
) {
for (event, event_id) in dmg_events.read_with_id() {
info!(
"Applying {} damage, triggered by {}",
event.amount, event_id.caller
);
…
```
```
2024-12-12T01:21:50.126827Z INFO event: Applying 9 damage, triggered by examples/ecs/event.rs:47:16
```
## Migration Guide
- If you manually construct a `SendEvent`, use `SendEvent::new()`
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Fixes#16771
## Solution
Fixed typo in code.
## Testing
- Did you test these changes? If so, how?
I tested on my own example, that I included in the issue. It was
behaving as I expected.
Here is the screenshot after fix, the screenshot before the fix can be
found in the issue.

# Objective
The doc comments and function namings for `BorderRect` feel imprecise to
me. Particularly the `square` function which is used to define a uniform
`BorderRect` with equal widths on each edge. But this is potentially
confusing since this "square" border could be around an oblong shape.
Using "padding" to refer to the border extents seems undesirable too
since "padding" is typically used to refer to the area between border
and content, not the border itself.
## Solution
* Rename `square` to `all` (this matches the name of the similar method
on `UiRect`).
* Rename `rectangle` to `axes` (this matches the name of the similar
method on `UiRect`).
* Update doc comments.
## Migration Guide
The `square` and `rectangle` functions belonging to `BorderRect` have
been renamed to `all` and `axes`.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit resolves most of the failures seen in #16670. It contains
two major fixes:
1. The prepass shaders weren't updated for bindless mode, so they were
accessing `material` as a single element instead of as an array. I added
the needed `BINDLESS` check.
2. If the mesh didn't support batch set keys (i.e. `get_batch_set_key()`
returns `None`), and multidraw was enabled, the batching logic would try
to multidraw all the meshes in a bin together instead of disabling
multidraw. This is because we checked whether the `Option<BatchSetKey>`
for the previous batch was equal to the `Option<BatchSetKey>` for the
next batch to determine whether objects could be multidrawn together,
which would return true if batch set keys were absent, causing an entire
bin to be multidrawn together. This patch fixes the logic so that
multidraw is only enabled if the batch set keys match *and are `Some`*.
Additionally, this commit adds batch key support for bins that use
`Opaque3dNoLightmapBinKey`, which in practice means prepasses.
Consequently, this patch enables multidraw for the prepass when GPU
culling is enabled.
When testing this patch, try adding `GpuCulling` to the camera in the
`deferred_rendering` and `ssr` examples. You can see that these examples
break without this patch and work properly with it.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Fixes#12359
## Solution
Implement alternative number 4.
https://github.com/bevyengine/bevy/issues/12359#issuecomment-2536422301
> I don't think that I agree with the premise of this issue anymore. I
am not sure that entities "magically" despawning themselves or
components removing themselves make for great defaults in an "ECS-based
API". This behavior is likely to be just as surprising to people.
>
> I think that the lack of sink re-usability should be treated as a bug
and possibly the documentation improved to reflect the current
limitations if it doesn't seem like a fix is forthcoming.
> -- me
# Objective
- Hides #16477.
## Solution
Add the advisory ID to the list of ignored advisories.
The notify-types crate has already been switched to web-time upstream,
but it's up to the maintainer to publish the crate. There is nothing for
us to do, so better to just ignore it so we don't ignore this CI check
anymore (and mistakenly miss new advisories).
## Testing
- Tested locally.
# Objective
Partially fixes#16736.
## Solution
`AnimatedField::new_unchecked` now supports tuple struct fields.
`animated_field!` is unchanged.
## Testing
Added a test to make sure common and simple uses of
`AnimatedField::new_unchecked` with tuple structs don't panic.
---------
Co-authored-by: yonzebu <yonzebu@gmail.com>
# Objective
Fixes#16645
## Solution
Keep track of components in callstack when registering required
components.
## Testing
Added a test checking that the error fires.
---
## Showcase
```rust
#[derive(Component, Default)]
#[require(B)]
struct A;
#[derive(Component, Default)]
#[require(A)]
struct B;
World::new().spawn(A);
```
```
thread 'main' panicked at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/component.rs:415:13:
Recursive required components detected: A → B → A
```
---------
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
# Objective
- Register `BoxShadow` type for reflection
## Testing
- Tested that box shadow example compiles and runs
## Additional
- It would be nice to have this in 0.15.1
# Objective
Fixes https://github.com/bevyengine/bevy/issues/16661
## Solution
- Update the doc links to point to the proper objects
## Testing
- Built crate docs and made sure the links worked locally
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Fixes https://github.com/bevyengine/bevy/issues/16752
## Solution
Renamed the 3 remaining instances of `enqueue_command` to
`queue_command`
## Testing
- Built locally
## Migration Guide
All instances of the `enqueue_command` method have been renamed to
`queue_command`.
# Objective
Draw the UI debug overlay using the UI renderer.
Significantly simpler and easier to use than
`bevy_dev_tools::ui_debug_overlay` which uses `bevy_gizmos`.
* Supports multiple windows and UI rendered to texture.
* Draws rounded debug rects for rounded UI nodes.
Fixes#16666
## Solution
Removed the `ui_debug_overlay` module from `bevy_dev_tools`.
Added a `bevy_ui_debug` feature gate.
Draw the UI debug overlay using the UI renderer.
Adds a new module `bevy_ui::render::debug_overlay`.
The debug overlay extraction function queries for the existing UI layout
and then adds a border around each UI node with `u32::MAX / 2` added to
each stack index so it's drawn on top.
There is a `UiDebugOptions` resource that can be used to enable or
disable the debug overlay and set the line width.
## Testing
The `testbed_ui` example has been changed to use the new debug overlay:
```
cargo run --example testbed_ui --features bevy_ui_debug
```
Press Space to toggle the debug overlay on and off.
---
## Showcase
<img width="961" alt="testbed-ui-new-debug"
src="https://github.com/user-attachments/assets/e9523d18-39ae-46a8-adbe-7d3f3ab8e951">
## Migration Guide
The `ui_debug_overlay` module has been removed from `bevy_dev_tools`.
There is a new debug overlay implemented using the `bevy_ui` renderer.
To use it, enable the `bevy_ui_debug` feature and set the `enable` field
of the `UiDebugOptions` resource to `true`.
# Objective
- Benchmarks are inconsistently setup, there are several that do not
compile, and several others that need to be reformatted.
- Related / precursor to #16647, this is part of my attempt migrating
[`bevy-bencher`](https://github.com/TheBevyFlock/bevy-bencher) to the
official benchmarks.
## Solution
> [!TIP]
>
> I recommend reviewing this PR commit-by-commit, instead of all at
once!
In 5d26f56eb9 I reorganized how benches
were registered. Now this is one `[[bench]]` per Bevy crate. In each
crate benchmark folder, there is a `main.rs` that calls
`criterion_main!`. I also disabled automatic benchmark discovery, which
isn't necessarily required, but may clear up confusion with our custom
setup. I also fixed a few errors that were causing the benchmarks to
fail to compile.
In afc8d33a87 I ran `rustfmt` on all of
the benchmarks.
In d6cdf960ab I fixed all of the Clippy
warnings.
In ee94d48f50 I fixed some of the
benchmarks' usage of `black_box()`. I ended up opening
https://github.com/rust-lang/rust/pull/133942 due to this, which should
help prevent this in the future.
In cbe1688dcd I renamed all of the ECS
benchmark groups to be called `benches`, to be consistent with the other
crate benchmarks.
In e701c212cd and
8815bb78b0 I re-ordered some imports and
module definitions, and uplifted `fragmentation/mod.rs` to
`fragementation.rs`.
Finally, in b0065e0b0b I organized
`Cargo.toml` and bumped Criterion to v0.5.
## Testing
- `cd benches && cargo clippy --benches`
- `cd benches && cargo fmt --all`
# Objective
- For curves that also include derivatives, make accessing derivative
information via the `Curve` API ergonomic: that is, provide access to a
curve that also samples derivative information.
- Implement this functionality for cubic spline curves provided by
`bevy_math`.
Ultimately, this is to serve the purpose of doing more geometric
operations on curves, like reparametrization by arclength and the
construction of moving frames.
## Solution
This has several parts, some of which may seem redundant. However, care
has been put into this to satisfy the following constraints:
- Accessing a `Curve` that samples derivative information should be not
just possible but easy and non-error-prone. For example, given a
differentiable `Curve<Vec2>`, one should be able to access something
like a `Curve<(Vec2, Vec2)>` ergonomically, and not just sample the
derivatives piecemeal from point to point.
- Derivative access should not step on the toes of ordinary curve usage.
In particular, in the above scenario, we want to avoid simply making the
same curve both a `Curve<Vec2>` and a `Curve<(Vec2, Vec2)>` because this
requires manual disambiguation when the API is used.
- Derivative access must work gracefully in both owned and borrowed
contexts.
### `HasTangent`
We introduce a trait `HasTangent` that provides an associated `Tangent`
type for types that have tangent spaces:
```rust
pub trait HasTangent {
/// The tangent type.
type Tangent: VectorSpace;
}
```
(Mathematically speaking, it would be more precise to say that these are
types that represent spaces which are canonically
[parallelized](https://en.wikipedia.org/wiki/Parallelizable_manifold). )
The idea here is that a point moving through a `HasTangent` type may
have a derivative valued in the associated `Tangent` type at each time
in its journey. We reify this with a `WithDerivative<T>` type that uses
`HasTangent` to include derivative information:
```rust
pub struct WithDerivative<T>
where
T: HasTangent,
{
/// The underlying value.
pub value: T,
/// The derivative at `value`.
pub derivative: T::Tangent,
}
```
And we can play the same game with second derivatives as well, since
every `VectorSpace` type is `HasTangent` where `Tangent` is itself (we
may want to be more restrictive with this in practice, but this holds
mathematically).
```rust
pub struct WithTwoDerivatives<T>
where
T: HasTangent,
{
/// The underlying value.
pub value: T,
/// The derivative at `value`.
pub derivative: T::Tangent,
/// The second derivative at `value`.
pub second_derivative: <T::Tangent as HasTangent>::Tangent,
}
```
In this PR, `HasTangent` is only implemented for `VectorSpace` types,
but it would be valuable to have this implementation for types like
`Rot2` and `Quat` as well. We could also do it for the isometry types
and, potentially, transforms as well. (This is in decreasing order of
value in my opinion.)
### `CurveWithDerivative`
This is a trait for a `Curve<T>` which allows the construction of a
`Curve<WithDerivative<T>>` when derivative information is known
intrinsically. It looks like this:
```rust
/// Trait for curves that have a well-defined notion of derivative, allowing for
/// derivatives to be extracted along with values.
pub trait CurveWithDerivative<T>
where
T: HasTangent,
{
/// This curve, but with its first derivative included in sampling.
fn with_derivative(self) -> impl Curve<WithDerivative<T>>;
}
```
The idea here is to provide patterns like this:
```rust
let value_and_derivative = my_curve.with_derivative().sample_clamped(t);
```
One of the main points here is that `Curve<WithDerivative<T>>` is useful
as an output because it can be used durably. For example, in a dynamic
context, something that needs curves with derivatives can store
something like a `Box<dyn Curve<WithDerivative<T>>>`. Note that
`CurveWithDerivative` is not dyn-compatible.
### `SampleDerivative`
Many curves "know" how to sample their derivatives instrinsically, but
implementing `CurveWithDerivative` as given would be onerous or require
an annoying amount of boilerplate. There are also hurdles to overcome
that involve references to curves: for the `Curve` API, the expectation
is that curve transformations like `with_derivative` take things by
value, with the contract that they can still be used by reference
through deref-magic by including `by_ref` in a method chain.
These problems are solved simultaneously by a trait `SampleDerivative`
which, when implemented, automatically derives `CurveWithDerivative` for
a type and all types that dereference to it. It just looks like this:
```rust
pub trait SampleDerivative<T>: Curve<T>
where
T: HasTangent,
{
fn sample_with_derivative_unchecked(&self, t: f32) -> WithDerivative<T>;
// ... other sampling variants as default methods
}
```
The point is that the output of `with_derivative` is a
`Curve<WithDerivative<T>>` that uses the `SampleDerivative`
implementation. On a `SampleDerivative` type, you can also just call
`my_curve.sample_with_derivative(t)` instead of something like
`my_curve.by_ref().with_derivative().sample(t)`, which is more verbose
and less accessible.
In practice, `CurveWithDerivative<T>` is actually a "sealed" extension
trait of `SampleDerivative<T>`.
## Adaptors
`SampleDerivative` has automatic implementations on all curve adaptors
except for `FunctionCurve`, `MapCurve`, and `ReparamCurve` (because we
do not have a notion of differentiable Rust functions).
For example, `CurveReparamCurve` (the reparametrization of a curve by
another curve) can compute derivatives using the chain rule in the case
both its constituents have them.
## Testing
Tests for derivatives on the curve adaptors are included.
---
## Showcase
This development allows derivative information to be included with and
extracted from curves using the `Curve` API.
```rust
let points = [
vec2(-1.0, -20.0),
vec2(3.0, 2.0),
vec2(5.0, 3.0),
vec2(9.0, 8.0),
];
// A cubic spline curve that goes through `points`.
let curve = CubicCardinalSpline::new(0.3, points).to_curve().unwrap();
// Calling `with_derivative` causes derivative output to be included in the output of the curve API.
let curve_with_derivative = curve.with_derivative();
// A `Curve<f32>` that outputs the speed of the original.
let speed_curve = curve_with_derivative.map(|x| x.derivative.norm());
```
---
## Questions
- ~~Maybe we should seal `WithDerivative` or make it require
`SampleDerivative` (i.e. make it unimplementable except through
`SampleDerivative`).~~ I decided this is a good idea.
- ~~Unclear whether `VectorSpace: HasTangent` blanket implementation is
really appropriate. For colors, for example, I'm not sure that the
derivative values can really be interpreted as a color. In any case, it
should still remain the case that `VectorSpace` types are `HasTangent`
and that `HasTangent::Tangent: HasTangent`.~~ I think this is fine.
- Infinity bikeshed on names of traits and things.
## Future
- Faster implementations of `SampleDerivative` for cubic spline curves.
- Improve ergonomics for accessing only derivatives (and other kinds of
transformations on derivative curves).
- Implement `HasTangent` for:
- `Rot2`/`Quat`
- `Isometry` types
- `Transform`, maybe
- Implement derivatives for easing curves.
- Marker traits for continuous/differentiable curves. (It's actually
unclear to me how much value this has in practice, but we have discussed
it in the past.)
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Updating dependencies; adopted version of #15696. (Supercedes #15696.)
Long answer: hashbrown is no longer using ahash by default, meaning that
we can't use the default-hasher methods with ahasher. So, we have to use
the longer-winded versions instead. This takes the opportunity to also
switch our default hasher as well, but without actually enabling the
default-hasher feature for hashbrown, meaning that we'll be able to
change our hasher more easily at the cost of all of these method calls
being obnoxious forever.
One large change from 0.15 is that `insert_unique_unchecked` is now
`unsafe`, and for cases where unsafe code was denied at the crate level,
I replaced it with `insert`.
## Migration Guide
`bevy_utils` has updated its version of `hashbrown` to 0.15 and now
defaults to `foldhash` instead of `ahash`. This means that if you've
hard-coded your hasher to `bevy_utils::AHasher` or separately used the
`ahash` crate in your code, you may need to switch to `foldhash` to
ensure that everything works like it does in Bevy.
# Objective
- Fixes#16497
- This is my first PR, so I'm still learning to contribute to the
project
## Solution
- Added struct `UnregisterSystemCached` and function
`unregister_system_cached`
- renamed `World::run_system_with_input` to `run_system_with`
- reordered input parameters for `World::run_system_once_with`
## Testing
- Added a crude test which registers a system via
`World::register_system_cached`, and removes it via
`Command::unregister_system_cached`.
## Migration Guide
- Change all occurrences of `World::run_system_with_input` to
`World::run_system_with`.
- swap the order of input parameters for `World::run_system_once_with`
such that the system comes before the input.
---------
Co-authored-by: Paul Mattern <mail@paulmattern.dev>
This commit makes skinned meshes batchable on platforms other than WebGL
2. On supported platforms, it replaces the two uniform buffers used for
joint matrices with a pair of storage buffers containing all matrices
for all skinned meshes packed together. The indices into the buffer are
stored in the mesh uniform and mesh input uniform. The GPU mesh
preprocessing step copies the indices in if that step is enabled.
On the `many_foxes` demo, I observed a frame time decrease from 15.470ms
to 11.935ms. This is the result of reducing the `submit_graph_commands`
time from an average of 5.45ms to 0.489ms, an 11x speedup in that
portion of rendering.

This is what the profile looks like for `many_foxes` after these
changes.

---------
Co-authored-by: François Mockers <mockersf@gmail.com>