# Objective
- Currently adding observers spawns an entity which implicitly flushes
the command queue, which can cause undefined behaviour if the
`WorldEntityMut` is used after this
- The reason `WorldEntityMut` attempted to (unsuccessfully) avoid
flushing commands until finished was that such commands may move or
despawn the entity being referenced, invalidating the cached location.
- With the introduction of hooks and observers, this isn't sensible
anymore as running the commands generated by hooks immediately is
required to maintain correct ordering of operations and to not expose
the world in an inconsistent state
- Objective is to make command flushing deterministic and fix the
related issues
- Fixes#16212
- Fixes#14621
- Fixes#16034
## Solution
- Allow `WorldEntityMut` to exist even when it refers to a despawned
entity by allowing `EntityLocation` to be marked invalid
- Add checks to all methods to panic if trying to access a despawned
entity
- Flush command queue after every operation that might trigger hooks or
observers
- Update entity location always after flushing command queue
## Testing
- Added test cases for currently broken behaviour
- Added test cases that flushes happen in all operations
- Added test cases to ensure hooks and commands are run exactly in
correct order when nested
---
Todo:
- [x] Write migration guide
- [x] Add tests that using `EntityWorldMut` on a despawned entity panics
- [x] Add tests that commands are flushed after every operation that is
supposed to flush them
- [x] Add tests that hooks, observers and their spawned commands are run
in the correct order when nested
---
## Migration Guide
Previously `EntityWorldMut` triggered command queue flushes in
unpredictable places, which could interfere with hooks and observers.
Now the command queue is flushed always immediately after any call in
`EntityWorldMut` that spawns or despawns an entity, or adds, removes or
replaces a component. This means hooks and observers will run their
commands in the correct order.
As a side effect, there is a possibility that a hook or observer could
despawn the entity that is being referred to by `EntityWorldMut`. This
could already currently happen if an observer was added while keeping an
`EntityWorldMut` referece and would cause unsound behaviour. If the
entity has been despawned, calling any methods which require the entity
location will panic. This matches the behaviour that `Commands` will
panic if called on an already despawned entity. In the extremely rare
case where taking a new `EntityWorldMut` reference or otherwise
restructuring the code so that this case does not happen is not
possible, there's a new `is_despawned` method that can be used to check
if the referred entity has been despawned.
# Objective
- Make it easier to understand how to profile things.
- Talk about CPU vs GPU work for graphics.
## Solution
- Add a section on GPU profiling and CPU vs GPU work.
- Rearrange some sections so Tracy is the first backend mentioned.
## Issues
I did this as a very quick fix to clear some things up, but the overall
guide still flows poorly and has too much extraneous info distracting
from the use case of "I just want to figure out why my app is slow",
where the advice should be "use tracy, and if GPU bottlenecked, do
this". Someone should do a full rewrite at some point.
I chose to omit talking about RenderDiagnosticsPlugin, but it's
definitely worth a mention as a way to easily check GPU + GPU time for
graphics work, although it's not hooked up in a lot of the engine, iirc
only shadows and the main passes. Again someone else should write about
it in the future.
Similarly it might've been useful to have a section describing how to
use NSight/RGP/IGA/Xcode for GPU profiling, as they're far from
intuitive tools.
# Objective
- Required by #16622 due to differing implementations of `System` by
`FunctionSystem` and `ExclusiveFunctionSystem`.
- Optimize the memory usage of instances of `apply_deferred` in system
schedules.
## Solution
By changing `apply_deferred` from being an ordinary system that ends up
as an `ExclusiveFunctionSystem`, and instead into a ZST struct that
implements `System` manually, we save ~320 bytes per instance of
`apply_deferred` in any schedule.
## Testing
- All current tests pass.
---
## Migration Guide
- If you were previously calling the special `apply_deferred` system via
`apply_deferred(world)`, don't.
# Objective
Outside of the `bevy_ecs` crate it's hard to implement `SystemParam`
trait on params that require access to the `World`, because `init_state`
expects user to extend access in `SystemMeta` and access-related fields
of `SystemMeta` are private.
## Solution
Expose those fields as a functions
# Objective
Define a framework for handling keyboard focus and bubbled keyboard
events, as discussed in #15374.
## Solution
Introduces a new crate, `bevy_input_focus`. This crate provides:
* A resource for tracking which entity has keyboard focus.
* Methods for getting and setting keyboard focus.
* Event definitions for triggering bubble-able keyboard input events to
the focused entity.
* A system for dispatching keyboard input events to the focused entity.
This crate does *not* provide any integration with UI widgets, or
provide functions for
tab navigation or gamepad-based focus navigation, as those are typically
application-specific.
## Testing
Most of the code has been copied from a different project, one that has
been well tested. However, most of what's in this module consists of
type definitions, with relatively small amounts of executable code. That
being said, I expect that there will be substantial bikeshedding on the
design, and I would prefer to hold off writing tests until after things
have settled.
I think that an example would be appropriate, however I'm waiting on a
few other pending changes to Bevy before doing so. In particular, I can
see a simple example with four buttons, with focus navigation between
them, and which can be triggered by the keyboard.
@alice-i-cecile
# Objective
- Fixes#16208
## Solution
- Added an associated type to `Component`, `Mutability`, which flags
whether a component is mutable, or immutable. If `Mutability= Mutable`,
the component is mutable. If `Mutability= Immutable`, the component is
immutable.
- Updated `derive_component` to default to mutable unless an
`#[component(immutable)]` attribute is added.
- Updated `ReflectComponent` to check if a component is mutable and, if
not, panic when attempting to mutate.
## Testing
- CI
- `immutable_components` example.
---
## Showcase
Users can now mark a component as `#[component(immutable)]` to prevent
safe mutation of a component while it is attached to an entity:
```rust
#[derive(Component)]
#[component(immutable)]
struct Foo {
// ...
}
```
This prevents creating an exclusive reference to the component while it
is attached to an entity. This is particularly powerful when combined
with component hooks, as you can now fully track a component's value,
ensuring whatever invariants you desire are upheld. Before this would be
done my making a component private, and manually creating a `QueryData`
implementation which only permitted read access.
<details>
<summary>Using immutable components as an index</summary>
```rust
/// This is an example of a component like [`Name`](bevy::prelude::Name), but immutable.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Component)]
#[component(
immutable,
on_insert = on_insert_name,
on_replace = on_replace_name,
)]
pub struct Name(pub &'static str);
/// This index allows for O(1) lookups of an [`Entity`] by its [`Name`].
#[derive(Resource, Default)]
struct NameIndex {
name_to_entity: HashMap<Name, Entity>,
}
impl NameIndex {
fn get_entity(&self, name: &'static str) -> Option<Entity> {
self.name_to_entity.get(&Name(name)).copied()
}
}
fn on_insert_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) {
let Some(&name) = world.entity(entity).get::<Name>() else {
unreachable!()
};
let Some(mut index) = world.get_resource_mut::<NameIndex>() else {
return;
};
index.name_to_entity.insert(name, entity);
}
fn on_replace_name(mut world: DeferredWorld<'_>, entity: Entity, _component: ComponentId) {
let Some(&name) = world.entity(entity).get::<Name>() else {
unreachable!()
};
let Some(mut index) = world.get_resource_mut::<NameIndex>() else {
return;
};
index.name_to_entity.remove(&name);
}
// Setup our name index
world.init_resource::<NameIndex>();
// Spawn some entities!
let alyssa = world.spawn(Name("Alyssa")).id();
let javier = world.spawn(Name("Javier")).id();
// Check our index
let index = world.resource::<NameIndex>();
assert_eq!(index.get_entity("Alyssa"), Some(alyssa));
assert_eq!(index.get_entity("Javier"), Some(javier));
// Changing the name of an entity is also fully capture by our index
world.entity_mut(javier).insert(Name("Steven"));
// Javier changed their name to Steven
let steven = javier;
// Check our index
let index = world.resource::<NameIndex>();
assert_eq!(index.get_entity("Javier"), None);
assert_eq!(index.get_entity("Steven"), Some(steven));
```
</details>
Additionally, users can use `Component<Mutability = ...>` in trait
bounds to enforce that a component _is_ mutable or _is_ immutable. When
using `Component` as a trait bound without specifying `Mutability`, any
component is applicable. However, methods which only work on mutable or
immutable components are unavailable, since the compiler must be
pessimistic about the type.
## Migration Guide
- When implementing `Component` manually, you must now provide a type
for `Mutability`. The type `Mutable` provides equivalent behaviour to
earlier versions of `Component`:
```rust
impl Component for Foo {
type Mutability = Mutable;
// ...
}
```
- When working with generic components, you may need to specify that
your generic parameter implements `Component<Mutability = Mutable>`
rather than `Component` if you require mutable access to said component.
- The entity entry API has had to have some changes made to minimise
friction when working with immutable components. Methods which
previously returned a `Mut<T>` will now typically return an
`OccupiedEntry<T>` instead, requiring you to add an `into_mut()` to get
the `Mut<T>` item again.
## Draft Release Notes
Components can now be made immutable while stored within the ECS.
Components are the fundamental unit of data within an ECS, and Bevy
provides a number of ways to work with them that align with Rust's rules
around ownership and borrowing. One part of this is hooks, which allow
for defining custom behavior at key points in a component's lifecycle,
such as addition and removal. However, there is currently no way to
respond to _mutation_ of a component using hooks. The reasons for this
are quite technical, but to summarize, their addition poses a
significant challenge to Bevy's core promises around performance.
Without mutation hooks, it's relatively trivial to modify a component in
such a way that breaks invariants it intends to uphold. For example, you
can use `core::mem::swap` to swap the components of two entities,
bypassing the insertion and removal hooks.
This means the only way to react to this modification is via change
detection in a system, which then begs the question of what happens
_between_ that alteration and the next run of that system?
Alternatively, you could make your component private to prevent
mutation, but now you need to provide commands and a custom `QueryData`
implementation to allow users to interact with your component at all.
Immutable components solve this problem by preventing the creation of an
exclusive reference to the component entirely. Without an exclusive
reference, the only way to modify an immutable component is via removal
or replacement, which is fully captured by component hooks. To make a
component immutable, simply add `#[component(immutable)]`:
```rust
#[derive(Component)]
#[component(immutable)]
struct Foo {
// ...
}
```
When implementing `Component` manually, there is an associated type
`Mutability` which controls this behavior:
```rust
impl Component for Foo {
type Mutability = Mutable;
// ...
}
```
Note that this means when working with generic components, you may need
to specify that a component is mutable to gain access to certain
methods:
```rust
// Before
fn bar<C: Component>() {
// ...
}
// After
fn bar<C: Component<Mutability = Mutable>>() {
// ...
}
```
With this new tool, creating index components, or caching data on an
entity should be more user friendly, allowing libraries to provide APIs
relying on components and hooks to uphold their invariants.
## Notes
- ~~I've done my best to implement this feature, but I'm not happy with
how reflection has turned out. If any reflection SMEs know a way to
improve this situation I'd greatly appreciate it.~~ There is an
outstanding issue around the fallibility of mutable methods on
`ReflectComponent`, but the DX is largely unchanged from `main` now.
- I've attempted to prevent all safe mutable access to a component that
does not implement `Component<Mutability = Mutable>`, but there may
still be some methods I have missed. Please indicate so and I will
address them, as they are bugs.
- Unsafe is an escape hatch I am _not_ attempting to prevent. Whatever
you do with unsafe is between you and your compiler.
- I am marking this PR as ready, but I suspect it will undergo fairly
major revisions based on SME feedback.
- I've marked this PR as _Uncontroversial_ based on the feature, not the
implementation.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
Co-authored-by: Nuutti Kotivuori <naked@iki.fi>
This commit allows the Bevy renderer to use the clustering
infrastructure for light probes (reflection probes and irradiance
volumes) on platforms where at least 3 storage buffers are available. On
such platforms (the vast majority), we stop performing brute-force
searches of light probes for each fragment and instead only search the
light probes with bounding spheres that intersect the current cluster.
This should dramatically improve scalability of irradiance volumes and
reflection probes.
The primary platform that doesn't support 3 storage buffers is WebGL 2,
and we continue using a brute-force search of light probes on that
platform, as the UBO that stores per-cluster indices is too small to fit
the light probe counts. Note, however, that that platform also doesn't
support bindless textures (indeed, it would be very odd for a platform
to support bindless textures but not SSBOs), so we only support one of
each type of light probe per drawcall there in the first place.
Consequently, this isn't a performance problem, as the search will only
have one light probe to consider. (In fact, clustering would probably
end up being a performance loss.)
Known potential improvements include:
1. We currently cull based on a conservative bounding sphere test and
not based on the oriented bounding box (OBB) of the light probe. This is
improvable, but in the interests of simplicity, I opted to keep the
bounding sphere test for now. The OBB improvement can be a follow-up.
2. This patch doesn't change the fact that each fragment only takes a
single light probe into account. Typical light probe implementations
detect the case in which multiple light probes cover the current
fragment and perform some sort of weighted blend between them. As the
light probe fetch function presently returns only a single light probe,
implementing that feature would require more code restructuring, so I
left it out for now. It can be added as a follow-up.
3. Light probe implementations typically have a falloff range. Although
this is a wanted feature in Bevy, this particular commit also doesn't
implement that feature, as it's out of scope.
4. This commit doesn't raise the maximum number of light probes past its
current value of 8 for each type. This should be addressed later, but
would possibly require more bindings on platforms with storage buffers,
which would increase this patch's complexity. Even without raising the
limit, this patch should constitute a significant performance
improvement for scenes that get anywhere close to this limit. In the
interest of keeping this patch small, I opted to leave raising the limit
to a follow-up.
## Changelog
### Changed
* Light probes (reflection probes and irradiance volumes) are now
clustered on most platforms, improving performance when many light
probes are present.
---------
Co-authored-by: Benjamin Brienen <Benjamin.Brienen@outlook.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Add a way to use the gizmo API in a retained manner, for increased
performance.
## Solution
- Move gizmo API from `Gizmos` to `GizmoBuffer`, ~ab~using `Deref` to
keep usage the same as before.
- Merge non-strip and strip variant of `LineGizmo` into one, storing the
data in a `GizmoBuffer` to have the same API for retained `LineGizmo`s.
### Review guide
- The meat of the changes are in `lib.rs`, `retained.rs`, `gizmos.rs`,
`pipeline_3d.rs` and `pipeline_2d.rs`
- The other files contain almost exclusively the churn from moving the
gizmo API from `Gizmos` to `GizmoBuffer`
## Testing
### Performance
Performance compared to the immediate mode API is from 65 to 80 times
better for static lines.
```
7900 XTX, 3700X
1707.9k lines/ms: gizmos_retained (21.3ms)
3488.5k lines/ms: gizmos_retained_continuous_polyline (31.3ms)
0.5k lines/ms: gizmos_retained_separate (97.7ms)
3054.9k lines/ms: bevy_polyline_retained_nan (16.8ms)
3596.3k lines/ms: bevy_polyline_retained_continuous_polyline (14.2ms)
0.6k lines/ms: bevy_polyline_retained_separate (78.9ms)
26.9k lines/ms: gizmos_immediate (14.9ms)
43.8k lines/ms: gizmos_immediate_continuous_polyline (18.3ms)
```
Looks like performance is good enough, being close to par with
`bevy_polyline`.
Benchmarks can be found here:
This branch:
https://github.com/tim-blackbird/line_racing/tree/retained-gizmos
Bevy 0.14: https://github.com/DGriffin91/line_racing
## Showcase
```rust
fn setup(
mut commands: Commands,
mut gizmo_assets: ResMut<Assets<GizmoAsset>>
) {
let mut gizmo = GizmoAsset::default();
// A sphere made out of one million lines!
gizmo
.sphere(default(), 1., CRIMSON)
.resolution(1_000_000 / 3);
commands.spawn(Gizmo {
handle: gizmo_assets.add(gizmo),
..default()
});
}
```
## Follow-up work
- Port over to the retained rendering world proper
- Calculate visibility and cull `Gizmo`s
# Objective
BrpQueryRow doesn't serialize `has` field if it is empty. That is okay
until you try to deserialize it after. Then it will fail to deserialize
due to missing field.
## Solution
Serde support using default value when field is missing, this PR adds
that.
# Objective
Fix a [Blenvy](https://github.com/kaosat-dev/Blenvy) crash due to a
missing type registration for `TextEntity` (as the type is used by
`ComputedTextBlock` but wasn't itself registered.)
## Solution
- Added the missing type registration
## Testing
- N/A
Currently, the prepass has no support for visibility ranges, so
artifacts appear when using dithering visibility ranges in conjunction
with a prepass. This patch fixes that problem.
Note that this patch changes the prepass to use sparse bind group
indices instead of sequential ones. I figured this is cleaner, because
it allows for greater sharing of WGSL code between the forward pipeline
and the prepass pipeline.
The `visibility_range` example has been updated to allow the prepass to
be toggled on and off.
# Objective
- Contributes to #15460
## Solution
- Removed `petgraph` as a dependency from the `bevy_ecs` crate.
- Replaced `TarjanScc` and `GraphMap` with specialised in-tree
alternatives.
## Testing
- Ran CI locally.
- Added new unit tests to check ordering invariants.
- Confirmed `petgraph` is no longer present in `cargo tree -p bevy_ecs`
## Migration Guide
The `Dag::graph` method no longer returns a `petgraph` `DiGraph` and
instead returns the new `DiGraph` type within `bevy_ecs`. Edge and node
iteration methods are provided so conversion to the `petgraph` type
should be trivial if required.
## Notes
- `indexmap` was already in the dependency graph for `bevy_ecs`, so its
inclusion here makes no difference to compilation time for Bevy.
- The implementation for `Graph` is heavily inspired from the `petgraph`
original, with specialisations added to simplify and improve the type.
- `petgraph` does have public plans for `no_std` support, however there
is no timeframe on if or when that functionality will be available.
Moving to an in-house solution in the interim allows Bevy to continue
developing its `no_std` offerings and further explore alternate graphing
options.
---------
Co-authored-by: Lixou <82600264+DasLixou@users.noreply.github.com>
Co-authored-by: vero <11307157+atlv24@users.noreply.github.com>
# Objective
- dont depend on wgpu if we dont have to
## Solution
- works towards this, but doesnt fully accomplish it. bevy_mesh depends
on bevy_image
## Testing
- 3d_scene runs
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com>
# Objective
- Fixes#16594
## Solution
- `StateTransitionSteps` was made public
## Testing
- _Did you test these changes? If so, how?_
I am now able to import it and use it.
---
## Showcase
- Users can now write their own state scoped resources
```rust
fn init_state_scoped_resource<R: Resource + FromWorld>(
&mut self,
state: impl States,
) -> &mut Self {
use bevy::state::state::StateTransitionSteps; // this PR in action
self.add_systems(
StateTransition,
(
clear_state_scoped_resource_impl::<_, R>(state.clone())
.in_set(StateTransitionSteps::ExitSchedules), // and here
init_state_scoped_resource_impl::<_, R>(state)
.in_set(StateTransitionSteps::EnterSchedules), // here too
),
);
self
}
```
# Objective
Make documentation of a component's required components more visible by
moving it to the type's docs
## Solution
Change `#[require]` from a derive macro helper to an attribute macro.
Disadvantages:
- this silences any unused code warnings on the component, as it is used
by the macro!
- need to import `require` if not using the ecs prelude (I have not
included this in the migration guilde as Rust tooling already suggests
the fix)
---
## Showcase
![Documentation of
Camera](https://github.com/user-attachments/assets/3329511b-747a-4c8d-a43e-57f7c9c71a3c)
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
# Objective
The `UiBoxShadowSamples` resource should be renamed to
`BoxShadowSamples` so it matches the `BoxShadow` component.
## Migration Guide
`UiBoxShadowSamples` has been renamed to `BoxShadowSamples`
# Objective
`flush_and_reserve_invalid_assuming_no_entities` was made for the old
rendering world (which was reset every frame) and is usused since the
0.15 retained rendering world, but wasn't removed yet. It is pub, but is
undocumented apart from the safety comment.
## Solution
Remove `flush_and_reserve_invalid_assuming_no_entities` and the safety
invariants this method required for `EntityMeta`, `EntityLocation`,
`TableId` and `TableRow`. This reduces the amount of unsafe code &
safety invariants and makes #16047 easier.
## Alternatives
- Document `flush_and_reserve_invalid_assuming_no_entities` and keep it
unchanged
- Document `flush_and_reserve_invalid_assuming_no_entities` and change
it to be based on `EntityMeta::INVALID`
## Migration Guide
- exchange `Entities::flush_and_reserve_invalid_assuming_no_entities`
for `reserve` and `flush_as_invalid` and notify us if that's
insufficient
---------
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
# Objective
Remove the `min` and `max` fields from `LayoutContext`.
It doesn't seem useful to cache these values, it's simpler just to call
`min_element` and `max_element` on the `physical_size`
field.
## Migration Guide
The `min` and `max` fields have been removed from `LayoutContext`. To
retrieve these values call `min_element` and `max_element` on
`LayoutContent::physical_size` instead.
# Objective
This was always a bit weird; `IntoIterator` is considered more idiomatic
in Rust.
The reason these used `Into<Vec<..>>` in the first place was (to my
knowledge) because of concerns that passing an already-owned vector
would cause a redundant allocation if the iterator API was used instead.
However, I have looked at simple examples for this scenario and the
generated assembly is identical (i.e. `into_iter().collect()` is
effectively converted to a no-op).
## Solution
As described in the title.
## Testing
It compiles. Ran existing tests.
## Migration Guide
The cubic splines API now uses `IntoIterator` in places where it used
`Into<Vec<..>>`. For most users, this will have little to no effect (it
is largely more permissive). However, in case you were using some
unusual input type that implements `Into<Vec<..>>` without implementing
`IntoIterator`, you can migrate by converting the input to a `Vec<..>`
before passing it into the interface.
# Objective
- Allow bevy_sprite_picking backend to pass through transparent sections
of the sprite.
- Fixes#14929
## Solution
- After sprite picking detects the cursor is within a sprites rect,
check the pixel at that location on the texture and check that it meets
an optional transparency cutoff. Change originally created for
mod_picking on bevy 0.14
(https://github.com/aevyrie/bevy_mod_picking/pull/373)
## Testing
- Ran Sprite Picking example to check it was working both with
transparency enabled and disabled
- ModPicking version is currently in use in my own isometric game where
this has been an extremely noticeable issue
## Showcase
![Sprite Picking
Text](https://github.com/user-attachments/assets/76568c0d-c359-422b-942d-17c84d3d3009)
## Migration Guide
Sprite picking now ignores transparent regions (with an alpha value less
than or equal to 0.1). To configure this, modify the
`SpriteBackendSettings` resource.
---------
Co-authored-by: andriyDev <andriydzikh@gmail.com>
This patch adds the infrastructure necessary for Bevy to support
*bindless resources*, by adding a new `#[bindless]` attribute to
`AsBindGroup`.
Classically, only a single texture (or sampler, or buffer) can be
attached to each shader binding. This means that switching materials
requires breaking a batch and issuing a new drawcall, even if the mesh
is otherwise identical. This adds significant overhead not only in the
driver but also in `wgpu`, as switching bind groups increases the amount
of validation work that `wgpu` must do.
*Bindless resources* are the typical solution to this problem. Instead
of switching bindings between each texture, the renderer instead
supplies a large *array* of all textures in the scene up front, and the
material contains an index into that array. This pattern is repeated for
buffers and samplers as well. The renderer now no longer needs to switch
binding descriptor sets while drawing the scene.
Unfortunately, as things currently stand, this approach won't quite work
for Bevy. Two aspects of `wgpu` conspire to make this ideal approach
unacceptably slow:
1. In the DX12 backend, all binding arrays (bindless resources) must
have a constant size declared in the shader, and all textures in an
array must be bound to actual textures. Changing the size requires a
recompile.
2. Changing even one texture incurs revalidation of all textures, a
process that takes time that's linear in the total size of the binding
array.
This means that declaring a large array of textures big enough to
encompass the entire scene is presently unacceptably slow. For example,
if you declare 4096 textures, then `wgpu` will have to revalidate all
4096 textures if even a single one changes. This process can take
multiple frames.
To work around this problem, this PR groups bindless resources into
small *slabs* and maintains a free list for each. The size of each slab
for the bindless arrays associated with a material is specified via the
`#[bindless(N)]` attribute. For instance, consider the following
declaration:
```rust
#[derive(AsBindGroup)]
#[bindless(16)]
struct MyMaterial {
#[buffer(0)]
color: Vec4,
#[texture(1)]
#[sampler(2)]
diffuse: Handle<Image>,
}
```
The `#[bindless(N)]` attribute specifies that, if bindless arrays are
supported on the current platform, each resource becomes a binding array
of N instances of that resource. So, for `MyMaterial` above, the `color`
attribute is exposed to the shader as `binding_array<vec4<f32>, 16>`,
the `diffuse` texture is exposed to the shader as
`binding_array<texture_2d<f32>, 16>`, and the `diffuse` sampler is
exposed to the shader as `binding_array<sampler, 16>`. Inside the
material's vertex and fragment shaders, the applicable index is
available via the `material_bind_group_slot` field of the `Mesh`
structure. So, for instance, you can access the current color like so:
```wgsl
// `uniform` binding arrays are a non-sequitur, so `uniform` is automatically promoted
// to `storage` in bindless mode.
@group(2) @binding(0) var<storage> material_color: binding_array<Color, 4>;
...
@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
let color = material_color[mesh[in.instance_index].material_bind_group_slot];
...
}
```
Note that portable shader code can't guarantee that the current platform
supports bindless textures. Indeed, bindless mode is only available in
Vulkan and DX12. The `BINDLESS` shader definition is available for your
use to determine whether you're on a bindless platform or not. Thus a
portable version of the shader above would look like:
```wgsl
#ifdef BINDLESS
@group(2) @binding(0) var<storage> material_color: binding_array<Color, 4>;
#else // BINDLESS
@group(2) @binding(0) var<uniform> material_color: Color;
#endif // BINDLESS
...
@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
#ifdef BINDLESS
let color = material_color[mesh[in.instance_index].material_bind_group_slot];
#else // BINDLESS
let color = material_color;
#endif // BINDLESS
...
}
```
Importantly, this PR *doesn't* update `StandardMaterial` to be bindless.
So, for example, `scene_viewer` will currently not run any faster. I
intend to update `StandardMaterial` to use bindless mode in a follow-up
patch.
A new example, `shaders/shader_material_bindless`, has been added to
demonstrate how to use this new feature.
Here's a Tracy profile of `submit_graph_commands` of this patch and an
additional patch (not submitted yet) that makes `StandardMaterial` use
bindless. Red is those patches; yellow is `main`. The scene was Bistro
Exterior with a hack that forces all textures to opaque. You can see a
1.47x mean speedup.
![Screenshot 2024-11-12
161713](https://github.com/user-attachments/assets/4334b362-42c8-4d64-9cfb-6835f019b95c)
## Migration Guide
* `RenderAssets::prepare_asset` now takes an `AssetId` parameter.
* Bin keys now have Bevy-specific material bind group indices instead of
`wgpu` material bind group IDs, as part of the bindless change. Use the
new `MaterialBindGroupAllocator` to map from bind group index to bind
group ID.
# Objective
Fixes#15941
## Solution
Created https://crates.io/crates/variadics_please and moved the code
there; updating references
`bevy_utils/macros` is deleted.
## Testing
cargo check
## Migration Guide
Use `variadics_please::{all_tuples, all_tuples_with_size}` instead of
`bevy::utils::{all_tuples, all_tuples_with_size}`.
## Objective
Fixes#1515
This PR implements a flexible entity cloning system. The primary use
case for it is to clone dynamically-generated entities.
Example:
```rs
#[derive(Component, Clone)]
pub struct Projectile;
#[derive(Component, Clone)]
pub struct Damage {
value: f32,
}
fn player_input(
mut commands: Commands,
projectiles: Query<Entity, With<Projectile>>,
input: Res<ButtonInput<KeyCode>>,
) {
// Fire a projectile
if input.just_pressed(KeyCode::KeyF) {
commands.spawn((Projectile, Damage { value: 10.0 }));
}
// Triplicate all active projectiles
if input.just_pressed(KeyCode::KeyT) {
for projectile in projectiles.iter() {
// To triplicate a projectile we need to create 2 more clones
for _ in 0..2{
commands.clone_entity(projectile)
}
}
}
}
```
## Solution
### Commands
Add a `clone_entity` command to create a clone of an entity with all
components that can be cloned. Components that can't be cloned will be
ignored.
```rs
commands.clone_entity(entity)
```
If there is a need to configure the cloning process (like set to clone
recursively), there is a second command:
```rs
commands.clone_entity_with(entity, |builder| {
builder.recursive(true)
});
```
Both of these commands return `EntityCommands` of the cloned entity, so
the copy can be modified afterwards.
### Builder
All these commands use `EntityCloneBuilder` internally. If there is a
need to clone an entity using `World` instead, it is also possible:
```rs
let entity = world.spawn(Component).id();
let entity_clone = world.spawn_empty().id();
EntityCloneBuilder::new(&mut world).clone_entity(entity, entity_clone);
```
Builder has methods to `allow` or `deny` certain components during
cloning if required and can be extended by implementing traits on it.
This PR includes two `EntityCloneBuilder` extensions:
`CloneEntityWithObserversExt` to configure adding cloned entity to
observers of the original entity, and `CloneEntityRecursiveExt` to
configure cloning an entity recursively.
### Clone implementations
By default, all components that implement either `Clone` or `Reflect`
will be cloned (with `Clone`-based implementation preferred in case
component implements both).
This can be overriden on a per-component basis:
```rs
impl Component for SomeComponent {
const STORAGE_TYPE: StorageType = StorageType::Table;
fn get_component_clone_handler() -> ComponentCloneHandler {
// Don't clone this component
ComponentCloneHandler::Ignore
}
}
```
### `ComponentCloneHandlers`
Clone implementation specified in `get_component_clone_handler` will get
registered in `ComponentCloneHandlers` (stored in
`bevy_ecs::component::Components`) at component registration time.
The clone handler implementation provided by a component can be
overriden after registration like so:
```rs
let component_id = world.components().component_id::<Component>().unwrap()
world.get_component_clone_handlers_mut()
.set_component_handler(component_id, ComponentCloneHandler::Custom(component_clone_custom))
```
The default clone handler for all components that do not explicitly
define one (or don't derive `Component`) is
`component_clone_via_reflect` if `bevy_reflect` feature is enabled, and
`component_clone_ignore` (noop) otherwise.
Default handler can be overriden using
`ComponentCloneHandlers::set_default_handler`
### Handlers
Component clone handlers can be used to modify component cloning
behavior. The general signature for a handler that can be used in
`ComponentCloneHandler::Custom` is as follows:
```rs
pub fn component_clone_custom(
world: &mut DeferredWorld,
entity_cloner: &EntityCloner,
) {
// implementation
}
```
The `EntityCloner` implementation (used internally by
`EntityCloneBuilder`) assumes that after calling this custom handler,
the `target` entity has the desired version of the component from the
`source` entity.
### Builder handler overrides
Besides component-defined and world-overriden handlers,
`EntityCloneBuilder` also has a way to override handlers locally. It is
mainly used to allow configuration methods like `recursive` and
`add_observers`.
```rs
// From observer clone handler implementation
impl CloneEntityWithObserversExt for EntityCloneBuilder<'_> {
fn add_observers(&mut self, add_observers: bool) -> &mut Self {
if add_observers {
self.override_component_clone_handler::<ObservedBy>(ComponentCloneHandler::Custom(
component_clone_observed_by,
))
} else {
self.remove_component_clone_handler_override::<ObservedBy>()
}
}
}
```
## Testing
Includes some basic functionality tests and doctests.
Performance-wise this feature is the same as calling `clone` followed by
`insert` for every entity component. There is also some inherent
overhead due to every component clone handler having to access component
data through `World`, but this can be reduced without breaking current
public API in a later PR.
# Objective
- Fixes#16078
## Solution
- Rename things to clarify that we _want_ unclipped depth for
directional light shadow views, and need some way of disabling the GPU's
builtin depth clipping
- Use DEPTH_CLIP_CONTROL instead of the fragment shader emulation on
supported platforms
- Pass only the clip position depth instead of the whole clip position
between vertex->fragment shader (no idea if this helps performance or
not, compiler might optimize it anyways)
- Meshlets
- HW raster always uses DEPTH_CLIP_CONTROL since it targets a more
limited set of platforms
- SW raster was not handling DEPTH_CLAMP_ORTHO correctly, it ended up
pretty much doing nothing.
- This PR made me realize that SW raster technically should have depth
clipping for all views that are not directional light shadows, but I
decided not to bother writing it. I'm not sure that it ever matters in
practice. If proven otherwise, I can add it.
## Testing
- Did you test these changes? If so, how?
- Lighting example. Both opaque (no fragment shader) and alpha masked
geometry (fragment shader emulation) are working with
depth_clip_control, and both work when it's turned off. Also tested
meshlet example.
- Are there any parts that need more testing?
- Performance. I can't figure out a good test scene.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Toggle depth_clip_control_supported in prepass/mod.rs line 323 to turn
this PR on or off.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
- Native
---
## Migration Guide
- `MeshPipelineKey::DEPTH_CLAMP_ORTHO` is now
`MeshPipelineKey::UNCLIPPED_DEPTH_ORTHO`
- The `DEPTH_CLAMP_ORTHO` shaderdef has been renamed to
`UNCLIPPED_DEPTH_ORTHO_EMULATION`
- `clip_position_unclamped: vec4<f32>` is now `unclipped_depth: f32`
# Objective
Avoid a premature normalize operation and get better smooth normals for
it.
## Inspiration
@IceSentry suggested `face_normal()` could have its normalize removed
based on [this article](https://iquilezles.org/articles/normals/) in PR
#16039.
## Solution
I did not want to change `face_normal()` to return a vector that's not
normalized. The name "normal" implies it'll be normalized. Instead I
added the `face_area_normal()` function, whose result is not normalized.
Its magnitude is equal two times the triangle's area. I've noted why
this is the case in its doc comment.
I changed `compute_smooth_normals()` from computing normals from
adjacent faces with equal weight to use the area of the faces as a
weight. This has the benefit of being cheaper computationally and
hopefully produces better normals.
The `compute_flat_normals()` is unchanged and still uses
`face_normal()`.
## Testing
One test was added which shows the bigger triangle having an effect on
the normal, but the previous test that uses the same size triangles is
unchanged.
**WARNING:** No visual test has been done yet. No example exists that
demonstrates the compute_smooth_normals(). Perhaps there's a good model
to demonstrate what the differences are. I would love to have some input
on this.
I'd suggest @IceSentry and @stepancheg to review this PR.
## Further Considerations
It's possible weighting normals by their area is not definitely better
than unweighted. It's possible there may be aesthetic reasons to prefer
one over the other. In such a case, we could offer two variants:
weighted or unweighted. Or we could offer another function perhaps like
this: `compute_smooth_normals_with_weights(|normal, area| 1.0)` which
would restore the original unweighted sum of normals.
---
## Showcase
Smooth normal calculation now weights adjacent face normals by their
area.
## Migration Guide
# Objective
- Exposes raw winit events making Bevy even more modular and powerful
for custom plugin developers (e.g. a custom render plugin).
XRef: https://github.com/bevyengine/bevy/issues/5977
It doesn't quite close the issue as sending events is not supported (or
not very useful to be precise). I would think that supporting that
requires some extra considerations by someone a bit more familiar with
the `bevy_winit` crate. That said, this PR could be a nice step forward.
## Solution
Emit `RawWinitWindowEvent` objects for each received event.
## Testing
I verified that the events are emitted using a basic test app. I don't
think it makes sense to solidify this behavior in one of the examples.
---
## Showcase
My example usage for a custom `egui_winit` integration:
```rust
for ev in winit_events.read() {
if ev.window_id == window.id {
let _ = egui_winit.on_window_event(&window, &ev.event);
}
}
```
---------
Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
# Objective
- Keep Taffy version up to date
Taffy 0.6 doesn't include a huge amount relevant to Bevy. But it does:
- Add the `box_sizing` style
- Expose the computed `margin` in layout
- Traitifies the `Style` struct, which opens up the possibility of using
Bevy's `Style` struct directly (although Bevy currently does some style
resolution at conversion time which would no longer be cached if it was
used directly).
- Have a few bug fixes in the layout algorithms
## Solution
- Upgrade Taffy to `0.6.0`
## Testing
- I've run the `grid` example. All looks good.
- More testing is probably warranted. We have had regressions from Taffy
upgrades before
- Having said that, most of the algorithm changes this cycle were driven
by fixing WPT tests run through the new Servo integration. So they're
possibly less likely than usual to cause regressions.
## Breaking changes
The only "breaking" change is adding a field to `Style`. Probably
doesn't bear mentioning?
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- Contributes to #15460
## Solution
- Added two new features, `std` (default) and `alloc`, gating `std` and
`alloc` behind them respectively.
- Added missing `f32` functions to `std_ops` as required. These `f32`
methods have been added to the `clippy.toml` deny list to aid in
`no_std` development.
## Testing
- CI
- `cargo clippy -p bevy_math --no-default-features --features libm
--target "x86_64-unknown-none"`
- `cargo test -p bevy_math --no-default-features --features libm`
- `cargo test -p bevy_math --no-default-features --features "libm,
alloc"`
- `cargo test -p bevy_math --no-default-features --features "libm,
alloc, std"`
- `cargo test -p bevy_math --no-default-features --features "std"`
## Notes
The following items require the `alloc` feature to be enabled:
- `CubicBSpline`
- `CubicBezier`
- `CubicCardinalSpline`
- `CubicCurve`
- `CubicGenerator`
- `CubicHermite`
- `CubicNurbs`
- `CyclicCubicGenerator`
- `RationalCurve`
- `RationalGenerator`
- `BoxedPolygon`
- `BoxedPolyline2d`
- `BoxedPolyline3d`
- `SampleCurve`
- `SampleAutoCurve`
- `UnevenSampleCurve`
- `UnevenSampleAutoCurve`
- `EvenCore`
- `UnevenCore`
- `ChunkedUnevenCore`
This requirement could be relaxed in certain cases, but I had erred on
the side of gating rather than modifying. Since `no_std` is a new set of
platforms we are adding support to, and the `alloc` feature is enabled
by default, this is not a breaking change.
---------
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>
Co-authored-by: Matty <2975848+mweatherley@users.noreply.github.com>
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
# Objective
~Blocked on #13417~
Motivation is the same as in #13417. If users can sort `QueryIter`, to
only makes sense to also allow them to use this functionality on
`QueryManyIter`.
## Solution
Also implement the sorts on `QueryManyIter`.
The implementation of the sorts themselves are mostly the same as with
`QueryIter` in #13417.
They differ in that they re-use the `entity_iter` passed to the
`iter_many`, and internally call `iter_many_unchecked_manual` on the
lens `QueryState` with it.
These methods also return a different struct, `QuerySortedManyIter`,
because there is no longer a guarantee of unique entities.
`QuerySortedManyIter` implements the various `Iterator` traits for
read-only iteration, as `QueryManyIter` does + `DoubleEndedIterator`.
For mutable iteration, there is both a `fetch_next` and a
`fetch_next_back` method. However, they only become available after the
user calls `collect_inner` on `QuerySortedManyIter` first. This collects
the inner `entity_iter` (this is the sorted one, **not** the original
the user passed) to drop all query lens items to avoid aliasing.
When TAITs are available this `collect_inner` could be hidden away,
until then it is unfortunately not possible to elide this without either
regressing read-only iteration, or introducing a whole new type, mostly
being a copy of `QuerySortedIter`.
As a follow-up we could add a `entities_all_unique` method to check
whether the entity list consists of only unique entities, and then
return a `QuerySortedIter` from it (under opaque impl Trait if need be),
*allowing mutable `Iterator` trait iteration* over what was originally
an `iter_many` call.
Such a method can also be added to `QueryManyIter`, albeit needing a
separate, new return type.
## Testing
I've switched the third example/doc test under `sort` out for one that
shows the collect_inner/fetch_next_back functionality, otherwise the
examples are the same as in #13417, adjusted to use `iter_many` instead
of `iter`.
The `query-iter-many-sorts` test checks for equivalence to the
underlying sorts.
The test after shows that these sorts *do not* panic after
`fetch`/`fetch_next` calls.
## Changelog
Added `sort`, `sort_unstable`, `sort_by`, `sort_unstable_by`,
`sort_by_key`, `sort_by_cached_key` to `QueryManyIter`.
Added `QuerySortedManyIter`.
# Objective
- dont depend on wgpu if we dont have to
## Solution
- works towards this, but doesnt fully accomplish it. the remaining
types stopping us from doing this need to be moved upstream, i will PR
this
## Testing
- 3d_scene runs
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
- Fix outdated link in README.md
Perhaps the link name should be updated as well since the article is no
longer labelled as "Contributor's Guide"?
Either way better to avoid making users click twice.
# Objective
Add support for multiple box shadows on a single `Node`.
## Solution
* Rename `BoxShadow` to `ShadowStyle` and remove its `Component` derive.
* Create a new `BoxShadow` component that newtypes a `Vec<ShadowStyle>`.
* Add a `new` constructor method to `BoxShadow` for single shadows.
* Change `extract_shadows` to iterate through a list of shadows per
node.
Render order is determined implicitly from the order of the shadows
stored in the `BoxShadow` component, back-to-front.
Might be more efficient to use a `SmallVec<[ShadowStyle; 1]>` for the
list of shadows but not sure if the extra friction is worth it.
## Testing
Added a node with four differently coloured shadows to the `box_shadow`
example.
---
## Showcase
```
cargo run --example box_shadow
```
<img width="460" alt="four-shadow"
src="https://github.com/user-attachments/assets/2f728c47-33b4-42e1-96ba-28a774b94b24">
## Migration Guide
Bevy UI now supports multiple shadows per node. A new struct
`ShadowStyle` is used to set the style for each shadow. And the
`BoxShadow` component is changed to a tuple struct wrapping a vector
containing a list of `ShadowStyle`s. To spawn a node with a single
shadow you can use the `new` constructor function:
```rust
commands.spawn((
Node::default(),
BoxShadow::new(
Color::BLACK.with_alpha(0.8),
Val::Percent(offset.x),
Val::Percent(offset.y),
Val::Percent(spread),
Val::Px(blur),
)
));
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- Fixes: #15663
## Solution
- Add an `is_forward_plane` method to `Aabb`, and a `contains_aabb`
method to `Frustum`.
## Test
- I have created a frustum with an offset along with three unit tests to
evaluate the `contains_aabb` algorithm.
## Explanation for the Test Cases
- To facilitate the code review, I will explain how the frustum is
created. Initially, we create a frustum without any offset and then
create a cuboid that is just contained within it.
<img width="714" alt="image"
src="https://github.com/user-attachments/assets/a9ac53a2-f8a3-4e09-b20b-4ee71b27a099">
- Secondly, we move the cuboid by 2 units along both the x-axis and the
y-axis to make it more general.
## Reference
- [Frustum
Culling](https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling#)
- [AABB Plane
intersection](https://gdbooks.gitbooks.io/3dcollisions/content/Chapter2/static_aabb_plane.html)
---------
Co-authored-by: IQuick 143 <IQuick143cz@gmail.com>
# Objective
- I got tired of calling `enable_state_scoped_entities`, and though it
would make more sense to define that at the place where the state is
defined
## Solution
- add a derive attribute `#[states(scoped_entities)]` when derive
`States` or `SubStates` that enables it automatically when adding the
state
## Testing
- Ran the examples using it, they still work
# Objective
There is currently no way of getting `QueryState` from `&World`, so it
is hard to, for example, iterate over all entities with a component,
only having `&World`.
## Solution
Add `try_new` function to `QueryState` that internally uses
`WorldQuery`'s `get_state`.
## Testing
No testing
# Objective
Combine the `Option<_>` state in `FunctionSystem` into a single `Option`
to provide clarity and save space.
## Solution
Simplifies `FunctionSystem`'s layout by using a single
`Option<FunctionSystemState>` for state that must be initialized before
running, and saves a byte by removing the need to store an enum tag.
Additionally, calling `System::run` on an uninitialized `System` will
now give a more descriptive message prior to verifying the `WorldId`.
## Testing
Ran CI checks locally.
# Objective
In c5742ff43e ZIndex::Local() and
ZIndex::Global() were replaced with ZIndex() and GlobalZIndex().
A comment was likely forgotten.
## Solution
- Remove the deprecated "::Local" in the comment.
# Objective
See title.
## Solution
Move `bevy_animation` import to where it is used.
## Testing
Compiled with and without `bevy_animation` feature enabled.
# Objective
- Avoid recreating the monitor every loop (temp fix until it's done
properly on winit side)
- Add a new `WinitSettings` preset for mobile that makes the winit loop
wait more and recommend its usage
Co-authored by: @BenjaminBrienen
# Objective
Fixes#16494. Closes#16539, which this replaces. Suggestions alone
weren't enough, so now we have a new PR!
---------
Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
# Objective
- In 0.14, ZIndex and GlobalZIndex where split from a shared enum into
separate components. There have been a few people confused by the
behavior of ZIndex when they really needed GlobalZIndex.
## Solution
- Update ZIndex docs to improve discoverability of GlobalZIndex.
---------
Co-authored-by: Benjamin Brienen <benjamin.brienen@outlook.com>