Commit Graph

7120 Commits

Author SHA1 Message Date
Mads Marquart
27b64c3bf1
Fix iOS simulator build (#19498)
# Objective

Fixes https://github.com/bevyengine/bevy/issues/18893.

## Solution

`bindgen` currently has issues with building Xcode 16.3 and above, due
to Clang being more strict about target triples. It was fixed in
https://github.com/rust-lang/rust-bindgen/pull/3182, but as a breaking
release, so affected dependencies have to be updated to either not
depend on `bindgen`, or depend on a fixed version of it:
- `tracing-oslog`: https://github.com/Absolucy/tracing-oslog/pull/12
- `coreaudio-sys`: https://github.com/RustAudio/coreaudio-sys/pull/114

## Testing

```sh
cargo build --target aarch64-apple-ios-sim
```

With Xcode 16.3 or 16.4.
2025-06-10 17:01:37 +00:00
Talin
57ddae1e93
Core button widget (#19366)
# Objective

Part of #19236

## Solution

Adds a new `bevy_core_widgets` crate containing headless widget
implementations. This PR adds a single `CoreButton` widget, more widgets
to be added later once this is approved.

## Testing

There's an example, ui/core_widgets.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-10 12:50:08 -04:00
Alice Cecile
030edbf3fe
Rename bevy_ecs::world::Entry to ComponentEntry (#19517)
# Objective

As discussed in #19285, some of our names conflict. `Entry` in bevy_ecs
is one of those overly general names.

## Solution

Rename this type (and the related types) to `ComponentEntry`.

---------

Co-authored-by: urben1680 <55257931+urben1680@users.noreply.github.com>
2025-06-10 01:12:40 +00:00
Alice Cecile
6ddd0f16a8
Component lifecycle reorganization and documentation (#19543)
# Objective

I set out with one simple goal: clearly document the differences between
each of the component lifecycle events via module docs.

Unfortunately, no such module existed: the various lifecycle code was
scattered to the wind.
Without a unified module, it's very hard to discover the related types,
and there's nowhere good to put my shiny new documentation.

## Solution

1. Unify the assorted types into a single
`bevy_ecs::component_lifecycle` module.
2. Write docs.
3. Write a migration guide.

## Testing

Thanks CI!

## Follow-up

1. The lifecycle event names are pretty confusing, especially
`OnReplace`. We should consider renaming those. No bikeshedding in my PR
though!
2. Observers need real module docs too :(
3. Any additional functional changes should be done elsewhere; this is a
simple docs and re-org PR.

---------

Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
2025-06-10 00:59:16 +00:00
Ayan Das
c35df3ca66
Added async_executor to the array of features enabled by multi_threaded within bevy_tasks crate to prevent compile-time error when default-features are disabled. (#19334)
## Objective

Fixes #19051.

---

## Solution
Originally implemented `compiler_error!()` within bevy_tasks/src/lib.rs
file to provide descriptive message regarding missing feature. However,
a cleaner approach was to add `async_executor` to the array of features
enabled by `multi_threaded` instead. This removes the need for users to
manually add `features = ["multi_threaded", "async_executor"]`
separately as needed to be done previously.

---

## Testing

These changes were tested using a minimal, external project designed to
specifically test whether the standalone `multi_threaded` feature for
`bevy_tasks` with `default-features` disabled worked as intended without
running into any compile-time error.

### How it was tested:
1. A `bevy_tasks_test` binary project was set up in an external
directory.
2. Its `Cargo.toml` was configured to depend on the local `bevy_tasks`,
explicitly disabling default features and enabling only
`multi_threaded`.
3. A simple `bevy_tasks_test/bin/bevy_crates_test.rs` was created and
configured within `Cargo.toml` file where bevy_tasks was set to the
local version of the crate with the modified changes and `cargo add
bevy_platform` was executed through the terminal since that dependency
is also needed to execute the sample examples.
4. Then both the `examples/busy_behavior.rs` and
`examples/idle_behavior.rs` code was added and tested with just the
`multi_threaded` feature was enabled and the code executed successfully.

### Results:
The code executed successfully for both examples where a threadPool was
utilized with 4 tasks and spawning 40 tasks that spin for 100ms.
Demonstrating how the threads finished executing all the tasks
simultaneously after a brief delay of less than a second (this is
referencing `bevy_tasks/examples/busy_behavior.rs`). Alongside the
second example where one thread per logical core was was utilized for a
single spinning task and aside from utilizing the single thread, system
was intended to remain idle as part of good practice when it comes to
handling small workloads. (this is referencing
`bevy_tasks/examples/idle_behavior.rs`).

### How to test:
Reviewers can easily verify this by:
1.  Checking out this PR.
2. Creating `cargo new bevy_tasks_test` and the `Cargo.toml` should look
something like this:
    ```toml
    # bevy/tests/compile_fail_tasks/Cargo.toml
    [package]
    name = "bevy_tasks_test"
    version = "0.1.0"
    edition = "2021"
    publish = false

    [dependencies]
    # path to bevy_tasks sub-crate to test locally
bevy_tasks = { path = "../../bevy_tasks", default-features = false,
features = ["multi_threaded"] }
    bevy_platform = "0.16.1"
    ```
3. Copying the examples within bevy_creates/examples one by one and
testing them separately within `src/main.rs` to check whether the
examples work.
4. Then simply running `cargo run` within the terminal should suffice to
test if the single `multi_threaded` feature works without the need for
separately adding `async_executor` as `multi_threaded` already uses
`async_executor` internally.

### Platforms tested:
macOS (aarch64). As a `#[cfg]` based compile-time check, behavior should
be consistent across platforms.
2025-06-10 00:54:46 +00:00
Guillaume Wafo-Tapa
0e805eb49c
Implement SystemCondition for systems returning Result<bool, BevyError> and Result<(), BevyError> (#19553)
# Objective

Fixes #19403
As described in the issue, the objective is to support the use of
systems returning `Result<(), BevyError>` and
`Result<bool, BevyError>` as run conditions. In these cases, the run
condition would hold on `Ok(())` and `Ok(true)` respectively.


## Solution

`IntoSystem<In, bool, M>` cannot be implemented for systems returning
`Result<(), BevyError>` and `Result<bool, BevyError>` as that would
conflict with their trivial implementation of the trait. That led me to
add a method to the sealed trait `SystemCondition` that does the
conversion. In the original case of a system returning `bool`, the
system is returned as is. With the new types, the system is combined
with `map()` to obtain a `bool`.

By the way, I'm confused as to why `SystemCondition` has a generic `In`
parameter as it is only ever used with `In = ()` as far as I can tell.

## Testing

I added a simple test for both type of system. That's minimal but it
felt enough. I could not picture the more complicated tests passing for
a run condition returning `bool` and failing for the new types.

## Doc

I documenting the change on the page of the trait. I had trouble wording
it right but I'm not sure how to improve it. The phrasing "the condition
returns `true`" which reads naturally is now technically incorrect as
the new types return a `Result`. However, the underlying condition
system that the implementing system turns into does indeed return
`bool`. But talking about the implementation details felt too much.
Another possibility is to use another turn of phrase like "the condition
holds" or "the condition checks out". I've left "the condition returns
`true`" in the documentation of `run_if` and the provided methods for
now.

I'm perplexed about the examples. In the first one, why not implement
the condition directly instead of having a system returning it? Is it
from a time of Bevy where you had to implement your conditions that way?
In that case maybe that should be updated. And in the second example I'm
missing the point entirely. As I stated above, I've only seen conditions
used in contexts where they have no input parameter. Here we create a
condition with an input parameter (cannot be used by `run_if`) and we
are using it with `pipe()` which actually doesn't need our system to
implement `SystemCondition`. Both examples are also calling
`IntoSystem::into_system` which should not be encouraged. What am I
missing?
2025-06-10 00:04:10 +00:00
Lucas Franca
f5f092f2f3
Fix new typos (#19562)
# Objective

Fix new typos found on new version of `typos` (#19551)

## Solution

Fix typos
2025-06-09 22:55:14 +00:00
andriyDev
0381a798e2
Delete System::component_access(). (#19496)
# Objective

- Cleanup related to #19495.

## Solution

- Delete `System::component_access()`. It is redundant with
`System::component_access_set().combined_access()`.

## Testing

- None. There are no callers of this function.
2025-06-09 22:54:52 +00:00
Zion Klinger
a4dd873df8
Fix uninlined format argument lint (#19561)
# Objective
Fixes #19370 

## Solution
Implemented the lint as suggested by clippy.

## Testing
CI tests passed and lint was resolved.
2025-06-09 20:53:19 +00:00
urben1680
1294b71e35
Introduce CheckChangeTicks event that is triggered by World::check_change_ticks (#19274)
# Objective

In the past I had custom data structures containing `Tick`s. I learned
that these need to be regularly checked to clamp them. But there was no
way to hook into that logic so I abandoned storing ticks since then.

Another motivation to open this up some more is to be more able to do a
correct implementation of `System::check_ticks`.

## Solution

Add `CheckChangeTicks` and trigger it in `World::check_change_ticks`.
Make `Tick::check_tick` public.

This event makes it possible to store ticks in components or resources
and have them checked.

I also made `Schedules::check_change_ticks` public so users can store
schedules in custom resources/components for whatever reasons.

## Testing

The logic boils down to a single `World::trigger` call and I don't think
this needs more tests.

## Alternatives

Making this obsolete like with #15683.

---

## Showcase

From the added docs:

```rs
use bevy_ecs::prelude::*;
use bevy_ecs::component::CheckChangeTicks;

#[derive(Resource)]
struct CustomSchedule(Schedule);

let mut world = World::new();
world.add_observer(|tick: Trigger<CheckChangeTicks>, mut schedule: ResMut<CustomSchedule>| {
    schedule.0.check_change_ticks(tick.get());
});
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-09 20:44:49 +00:00
Marcel Müller
2768af5d2d
Allow not emitting BundleFromComponents with Bundle derive macro (#19249)
# Objective

Fixes #19136

## Solution

- Add a new container attribute which when set does not emit
`BundleFromComponents`

## Testing

- Did you test these changes?

Yes, a new test was added.

- Are there any parts that need more testing?

Since `BundleFromComponents` is unsafe I made extra sure that I did not
misunderstand its purpose. As far as I can tell, _not_ implementing it
is ok.

- How can other people (reviewers) test your changes? Is there anything
specific they need to know?

Nope

- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?

I don't think the platform is relevant

---



One thing I am not sure about is how to document this? I'll gladly add
it

---------

Signed-off-by: Marcel Müller <neikos@neikos.email>
2025-06-09 20:15:42 +00:00
Erick Z
b6e4d171b5
Adding PartialEq to some UI and Text types (#19552)
# Objective

- During the development of
[`bevy_flair`](https://github.com/eckz/bevy_flair) I found [some types
lacking
`PartialEq`](https://github.com/search?q=repo%3Aeckz%2Fbevy_flair%20%20%22Try%20to%20upstream%20it%20to%20bevy%22&type=code)
which made the unit testing a little bit inconvinient.

## Solution

- Adding `PartialEq` for the following types:
  - `LineHeight `
  - `TextShadow`
  - `NodeImageMode`

## Testing

- Letting github actions do the testing, this is not an invasive change
and `cargo run --bin ci` doesn't seem to work in `main` at the moment.
2025-06-09 20:08:17 +00:00
JMS55
476e644a7d
Add extra buffer usages field to MeshAllocator (#19546)
Split off from https://github.com/bevyengine/bevy/pull/19058
2025-06-09 20:03:57 +00:00
Alice Cecile
155ebf7898
Write real docs for SystemSet (#19538)
# Objective

`SystemSet`s are surprisingly rich and nuanced, but are extremely poorly
documented.

Fixes #19536.

## Solution

Explain the basic concept of system sets, how to create them, and give
some opinionated advice about their more advanced functionality.

## Follow-up

I'd like proper module level docs on system ordering that I can link to
here, but they don't exist. Punting to follow-up!

---------

Co-authored-by: theotherphil <phil.j.ellison@gmail.com>
2025-06-09 20:03:02 +00:00
ickshonpe
02fa833be1
Rename JustifyText to Justify (#19522)
# Objective

Rename `JustifyText`:

* The name `JustifyText` is just ugly.
* It's inconsistent since no other `bevy_text` types have a `Text-`
suffix, only prefix.
* It's inconsistent with the other text layout enum `Linebreak` which
doesn't have a prefix or suffix.

Fixes #19521.

## Solution

Rename `JustifyText` to `Justify`.

Without other context, it's natural to assume the name `Justify` refers
to text justification.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-09 19:59:48 +00:00
SpecificProtagonist
437c4d5b25
Minor clear color doc improvements (#19514) 2025-06-09 19:56:58 +00:00
theotherphil
5279863c42
Add missing docs for ImageLoader (#19499)
# Objective

Yet another tiny step towards
https://github.com/bevyengine/bevy/issues/3492.

---------

Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net>
2025-06-09 19:46:33 +00:00
andriyDev
f163649b48
Use component_access_set to determine the set of conflicting accesses between two systems. (#19495)
# Objective

- Fixes #4381

## Solution

- Replace `component_access` with `component_access_set` when
determining conflicting systems during schedule building.
- All `component_access()` impls just forward to
`&component_access_set().combined_access`, so we are essentially trading
`Access::is_compatible` for `FilteredAccessSet::is_compatible`.
- `FilteredAccessSet::get_conflicts` internally calls
`combined_access.is_compatible` as the first step, so we can remove that
redundant check.

## Testing

- Un-ignored a previously failing test now that it passes!
- Ran the `build_schedule` benchmark and got basically no change in the
results. Perhaps are benchmarks are just not targetted towards this
situation.
```
$ critcmp main fix-ambiguity -f 'build_schedule'
group                                          fix-ambiguity                          main
-----                                          -------------                          ----
build_schedule/1000_schedule                   1.00       2.9±0.02s        ? ?/sec    1.01       2.9±0.05s        ? ?/sec
build_schedule/1000_schedule_no_constraints    1.02     48.3±1.48ms        ? ?/sec    1.00     47.4±1.78ms        ? ?/sec
build_schedule/100_schedule                    1.00      9.9±0.17ms        ? ?/sec    1.06     10.5±0.32ms        ? ?/sec
build_schedule/100_schedule_no_constraints     1.00   804.7±21.85µs        ? ?/sec    1.03   828.7±19.36µs        ? ?/sec
build_schedule/500_schedule                    1.00    451.7±7.25ms        ? ?/sec    1.04   468.9±11.70ms        ? ?/sec
build_schedule/500_schedule_no_constraints     1.02     12.7±0.46ms        ? ?/sec    1.00     12.5±0.44ms        ? ?/sec
```
2025-06-09 19:40:52 +00:00
Eagster
064e5e48b4
Remove entity placeholder from observers (#19440)
# Objective

`Entity::PLACEHOLDER` acts as a magic number that will *probably* never
really exist, but it certainly could. And, `Entity` has a niche, so the
only reason to use `PLACEHOLDER` is as an alternative to `MaybeUninit`
that trades safety risks for logic risks.

As a result, bevy has generally advised against using `PLACEHOLDER`, but
we still use if for a lot internally. This pr starts removing internal
uses of it, starting from observers.

## Solution

Change all trigger target related types from `Entity` to
`Option<Entity>`

Small migration guide to come.

## Testing

CI

## Future Work

This turned a lot of code from 

```rust
trigger.target()
```

to 

```rust
trigger.target().unwrap()
```

The extra panic is no worse than before; it's just earlier than
panicking after passing the placeholder to something else.

But this is kinda annoying. 

I would like to add a `TriggerMode` or something to `Event` that would
restrict what kinds of targets can be used for that event. Many events
like `Removed` etc, are always triggered with a target. We can make
those have a way to assume Some, etc. But I wanted to save that for a
future pr.
2025-06-09 19:37:56 +00:00
SpecificProtagonist
860ff7819b
Remove workaround for image resize warning (#19397)
# Objective

Follow up for #19116 and #19098

## Testing

viewport_node example
2025-06-09 19:32:57 +00:00
Sigma-dev
8cd53162bf
Add a despawn_children method to EntityWorldMut and EntityCommands (#19283)
# Objective

At the moment, if someone wants to despawn all the children of an
entity, they would need to use `despawn_related::<Children>();`.
In my opinion, this makes a very common operation less easily
discoverable and require some understanding of Entity Relationships.

## Solution

Adding a `despawn_children ` makes a very simple, discoverable and
readable way to despawn all the children while maintaining cohesion with
other similar methods.

## Testing

The implementation itself is very simple as it simply wraps around
`despawn_related` with `Children` as the generic type.
I gave it a quick try by modifying the parenting example and it worked
as expected.

---------

Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2025-06-09 19:31:40 +00:00
theotherphil
20cd383b31
Mention Mut in QueryData docs, clarify behaviour of Mut vs &mut in Mut docs (#19254)
# Objective

- Fix https://github.com/bevyengine/bevy/issues/13843
- Clarify the difference between Mut and &mut when accessing query data

## Solution

- Mention `Mut` in `QueryData` docs as an example of a type that
implements this trait
- Give example of `iter_mut` vs `iter` access to `Mut` and `& mut`
parameters

## Testing

-
2025-06-09 19:21:04 +00:00
theotherphil
2bda628ecf
Clarify docs for transmute_lens functions (#19233)
# Objective

Make the restrictions of `transmute_lens` and related functions clearer.

Related issue: https://github.com/bevyengine/bevy/issues/12156
Related PR: https://github.com/bevyengine/bevy/pull/12157

## Solution

* Make it clearer that the set of returned entities is a subset of those
from the original query
* Move description of read/write/required access to a table
* Reference the new table in `transmute_lens` docs from the other
`transmute_lens*` functions

## Testing

cargo doc --open locally to check this render correctly

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
2025-06-09 19:10:59 +00:00
ickshonpe
4836c7868c
Specialized UI transform (#16615)
# Objective

Add specialized UI transform `Component`s and fix some related problems:
* Animating UI elements by modifying the `Transform` component of UI
nodes doesn't work very well because `ui_layout_system` overwrites the
translations each frame. The `overflow_debug` example uses a horrible
hack where it copies the transform into the position that'll likely
cause a panic if any users naively copy it.
* Picking ignores rotation and scaling and assumes UI nodes are always
axis aligned.
* The clipping geometry stored in `CalculatedClip` is wrong for rotated
and scaled elements.
* Transform propagation is unnecessary for the UI, the transforms can be
updated during layout updates.
* The UI internals use both object-centered and top-left-corner-based
coordinates systems for UI nodes. Depending on the context you have to
add or subtract the half-size sometimes before transforming between
coordinate spaces. We should just use one system consistantly so that
the transform can always be directly applied.
* `Transform` doesn't support responsive coordinates.

## Solution

* Unrequire `Transform` from `Node`.
* New components `UiTransform`, `UiGlobalTransform`:
- `Node` requires `UiTransform`, `UiTransform` requires
`UiGlobalTransform`
- `UiTransform` is a 2d-only equivalent of `Transform` with a
translation in `Val`s.
- `UiGlobalTransform` newtypes `Affine2` and is updated in
`ui_layout_system`.
* New helper functions on `ComputedNode` for mapping between viewport
and local node space.
* The cursor position is transformed to local node space during picking
so that it respects rotations and scalings.
* To check if the cursor hovers a node recursively walk up the tree to
the root checking if any of the ancestor nodes clip the point at the
cursor. If the point is clipped the interaction is ignored.
* Use object-centered coordinates for UI nodes.
* `RelativeCursorPosition`'s coordinates are now object-centered with
(0,0) at the the center of the node and the corners at (±0.5, ±0.5).
* Replaced the `normalized_visible_node_rect: Rect` field of
`RelativeCursorPosition` with `cursor_over: bool`, which is set to true
when the cursor is over an unclipped point on the node. The visible area
of the node is not necessarily a rectangle, so the previous
implementation didn't work.

This should fix all the logical bugs with non-axis aligned interactions
and clipping. Rendering still needs changes but they are far outside the
scope of this PR.

Tried and abandoned two other approaches:
* New `transform` field on `Node`, require `GlobalTransform` on `Node`,
and unrequire `Transform` on `Node`. Unrequiring `Transform` opts out of
transform propagation so there is then no conflict with updating the
`GlobalTransform` in `ui_layout_system`. This was a nice change in its
simplicity but potentially confusing for users I think, all the
`GlobalTransform` docs mention `Transform` and having special rules for
how it's updated just for the UI is unpleasently surprising.
* New `transform` field on `Node`. Unrequire `Transform` on `Node`. New
`transform: Affine2` field on `ComputedNode`.
This was okay but I think most users want a separate specialized UI
transform components. The fat `ComputedNode` doesn't work well with
change detection.

Fixes #18929, #18930

## Testing

There is an example you can look at: 
```
cargo run --example ui_transform
```

Sometimes in the example if you press the rotate button couple of times
the first glyph from the top label disappears , I'm not sure what's
causing it yet but I don't think it's related to this PR.

##  Migration Guide
New specialized 2D UI transform components `UiTransform` and
`UiGlobalTransform`. `UiTransform` is a 2d-only equivalent of
`Transform` with a translation in `Val`s. `UiGlobalTransform` newtypes
`Affine2` and is updated in `ui_layout_system`.
`Node` now requires `UiTransform` instead of `Transform`. `UiTransform`
requires `UiGlobalTransform`.

In previous versions of Bevy `ui_layout_system` would overwrite UI
node's `Transform::translation` each frame. `UiTransform`s aren't
overwritten and there is no longer any need for systems that cache and
rewrite the transform for translated UI elements.

`RelativeCursorPosition`'s coordinates are now object-centered with
(0,0) at the the center of the node and the corners at (±0.5, ±0.5). Its
`normalized_visible_node_rect` field has been removed and replaced with
a new `cursor_over: bool` field which is set to true when the cursor is
hovering an unclipped area of the UI node.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-09 19:05:49 +00:00
JMS55
bf8868b7b7
Require naga_oil 0.17.1 (#19550)
Split off from https://github.com/bevyengine/bevy/pull/19058

The patch should've been picked up anyways, but now it's required.
2025-06-09 04:54:29 +00:00
JMS55
16440be327
Add CameraMainTextureUsages helper method (#19549)
Split off from https://github.com/bevyengine/bevy/pull/19058
2025-06-09 04:54:14 +00:00
JMS55
ec307bcb9f
Add more wgpu helpers/types (#19548)
Split off from https://github.com/bevyengine/bevy/pull/19058
2025-06-09 04:54:02 +00:00
re0312
56f26cfb02
Unify system state (#19506)
# Objective

- A preparation for the 'system as entities'
- The current system has a series of states such as `is_send`,
`is_exclusive`, `has_defered`, As `system as entites` landed, it may
have more states. Using Bitflags to unify all states is a more concise
and performant approach

## Solution

- Using Bitflags to  unify system state.
2025-06-08 18:18:43 +00:00
Kristoffer Søholm
2c37bdeb47
Fix PickingInteraction change detection (#19488)
# Objective

Fixes #19464

## Solution

Instead of clearing previous `PickingInteractions` before updating, we
clear them last for those components that weren't updated, and use
`set_if_neq` when writing.

## Testing

I tried the sprite_picking example and it still works. 

You can add the following system to picking examples to check that
change detection works as intended:

```rust
fn print_picking(query: Query<(Entity, &PickingInteraction), Changed<PickingInteraction>>) {
    for (entity, interaction) in &query {
        println!("{entity} {interaction:?}");
    }
}
```
2025-06-08 16:29:13 +00:00
theotherphil
6b5289bd5e
deny(missing_docs) for bevy_ecs_macros (#19523)
# Objective

Deny missing docs for bevy_ecs_macros, towards
https://github.com/bevyengine/bevy/issues/3492.

## Solution

More docs of the form

```
/// Does the thing
fn do_the_thing() {}
```

But I don't think the derive macros are where anyone is going to be
looking for details of these concepts and deny(missing_docs) inevitably
results in some items having noddy docs.
2025-06-08 16:28:31 +00:00
JoshValjosh
ddee5cca85
Improve Bevy's double-precision story for third-party crates (#19194)
# Objective

Certain classes of games, usually those with enormous worlds, require
some amount of support for double-precision. Libraries like `big_space`
exist to allow for large worlds while integrating cleanly with Bevy's
primarily single-precision ecosystem, but even then, games will often
still work directly in double-precision throughout the part of the
pipeline that feeds into the Bevy interface.

Currently, working with double-precision types in Bevy is a pain. `glam`
provides types like `DVec3`, but Bevy doesn't provide double-precision
analogs for `glam` wrappers like `Dir3`. This is mostly because doing so
involves one of:

- code duplication
- generics
- templates (like `glam` uses)
- macros

Each of these has issues that are enough to be deal-breakers as far as
maintainability, usability or readability. To work around this, I'm
putting together `bevy_dmath`, a crate that duplicates `bevy_math` types
and functionality to allow downstream users to enjoy the ergonomics and
power of `bevy_math` in double-precision. For the most part, it's a
smooth process, but in order to fully integrate, there are some
necessary changes that can only be made in `bevy_math`.

## Solution

This PR addresses the first and easiest issue with downstream
double-precision math support: `VectorSpace` currently can only
represent vector spaces over `f32`. This automatically closes the door
to double-precision curves, among other things. This restriction can be
easily lifted by allowing vector spaces to specify the underlying scalar
field. This PR adds a new trait `ScalarField` that satisfies the
properties of a scalar field (the ones that can be upheld statically)
and adds a new associated type `type Scalar: ScalarField` to
`VectorSpace`. It's mostly an unintrusive change. The biggest annoyances
are:

- it touches a lot of curve code
- `bevy_math::ops` doesn't support `f64`, so there are some annoying
workarounds

As far as curves code, I wanted to make this change unintrusive and
bite-sized, so I'm trying to touch as little code as possible. To prove
to myself it can be done, I went ahead and (*not* in this PR) migrated
most of the curves API to support different `ScalarField`s and it went
really smoothly! The ugliest thing was adding `P::Scalar: From<usize>`
in several places. There's an argument to be made here that we should be
using `num-traits`, but that's not immediately relevant. The point is
that for now, the smallest change I could make was to go into every
curve impl and make them generic over `VectorSpace<Scalar = f32>`.
Curves work exactly like before and don't change the user API at all.

# Follow-up

- **Extend `bevy_math::ops` to work with `f64`.** `bevy_math::ops` is
used all over, and if curves are ever going to support different
`ScalarField` types, we'll need to be able to use the correct `std` or
`libm` ops for `f64` types as well. Adding an `ops64` mod turned out to
be really ugly, but I'll point out the maintenance burden is low because
we're not going to be adding new floating-point ops anytime soon.
Another solution is to build a floating-point trait that calls the right
op variant and impl it for `f32` and `f64`. This reduces maintenance
burden because on the off chance we ever *do* want to go modify it, it's
all tied together: you can't change the interface on one without
changing the trait, which forces you to update the other. A third option
is to use `num-traits`, which is basically option 2 but someone else did
the work for us. They already support `no_std` using `libm`, so it would
be more or less a drop-in replacement. They're missing a couple
floating-point ops like `floor` and `ceil`, but we could make our own
floating-point traits for those (there's even the potential for
upstreaming them into `num-traits`).
- **Tweak curves to accept vector spaces over any `ScalarField`.**
Curves are ready to support custom scalar types as soon as the bullet
above is addressed. I will admit that the code is not as fun to look at:
`P::Scalar` instead of `f32` everywhere. We could consider an alternate
design where we use `f32` even to interpolate something like a `DVec3`,
but personally I think that's a worse solution than parameterizing
curves over the vector space's scalar type. At the end of the day, it's
not really bad to deal with in my opinion... `ScalarType` supports
enough operations that working with them is almost like working with raw
float types, and it unlocks a whole ecosystem for games that want to use
double-precision.
2025-06-08 02:02:47 +00:00
urben1680
76b8310da5
Replace (Partial)Ord for EntityGeneration with corrected standalone method (#19432)
# Objective

#19421 implemented `Ord` for `EntityGeneration` along the lines of [the
impl from
slotmap](https://docs.rs/slotmap/latest/src/slotmap/util.rs.html#8):
```rs
/// Returns if a is an older version than b, taking into account wrapping of
/// versions.
pub fn is_older_version(a: u32, b: u32) -> bool {
    let diff = a.wrapping_sub(b);
    diff >= (1 << 31)
}
```

But that PR and the slotmap impl are different:

**slotmap impl**
- if `(1u32 << 31)` is greater than `a.wrapping_sub(b)`, then `a` is
older than `b`
- if `(1u32 << 31)` is equal to `a.wrapping_sub(b)`, then `a` is older
than `b`
- if `(1u32 << 31)` is less than `a.wrapping_sub(b)`, then `a` is equal
or newer than `b`

**previous PR impl**
- if `(1u32 << 31)` is greater than `a.wrapping_sub(b)`, then `a` is
older than `b`
- if `(1u32 << 31)` is equal to `a.wrapping_sub(b)`, then `a` is equal
to `b` ⚠️
- if `(1u32 << 31)` is less than `a.wrapping_sub(b)`, then `a` is newer
than `b` ⚠️

This ordering is also not transitive, therefore it should not implement
`PartialOrd`.

## Solution

Fix the impl in a standalone method, remove the `Partialord`/`Ord`
implementation.

## Testing

Given the first impl was wrong and got past reviews, I think a new unit
test is justified.
2025-06-07 22:29:13 +00:00
andriyDev
de79d3f363
Mention in the docs for pointer events that these are in screen-space. (#19518)
# Objective

- Fixes #18109.

## Solution

- All these docs now mention screen-space vs world-space.
- `start_pos` and `latest_pos` both link to `viewport_to_world` and
`viewport_to_world_2d`.
- The remaining cases are all deltas. Unfortunately `Camera` doesn't
have an appropriate method for these cases, and implementing one would
be non-trivial (e.g., the delta could have a different world-space size
based on the depth). For these cases, I just link to `Camera` and
suggest using some of its methods. Not a great solution, but at least it
gets users on the correct track.
2025-06-06 22:20:14 +00:00
Alice Cecile
e1230fdc54
Remove re-exports of cosmic_text types (#19516)
# Objective

As discussed in #19285, we do a poor job at keeping the namespace tidy
and free of duplicates / user-conflicting names in places. `cosmic_text`
re-exports were the worst offender.

## Solution

Remove the re-exports completely. While the type aliases were quite
thoughtful, they weren't used in any of our code / API.
2025-06-06 21:49:02 +00:00
re0312
5df9c53977
Fix regression on the get/get_mut/get_not_found (#19505)
# Objective

- Partial fix #19504
- As more features were added to Bevy ECS, certain core hot-path
function calls exceeded LLVM's automatic inlining threshold, leading to
significant performance regressions in some cases.



## Solution

- inline more functions.


## Performance
This brought nearly 3x improvement in Windows bench (using Sander's
testing code)

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-06 21:04:27 +00:00
Eagster
8ad7118443
Only get valid component ids (#19510)
# Objective

- #19504 showed a 11x regression in getting component values for
unregistered components. This pr should fix that and improve others a
little too.
- This is some cleanup work from #18173 .

## Solution

- Whenever we expect a component value to exist, we only care about
fully registered components, not queued to be registered components
since, for the value to exist, it must be registered.
- So we can use the faster `get_valid_*` instead of `get_*` in a lot of
places.
- Also found a bug where `valid_*` did not forward to `get_valid_*`
properly. That's fixed.

## Testing

CI
2025-06-06 20:59:57 +00:00
robtfm
3dc6a07d27
generic component propagation (#17575)
# Objective

add functionality to allow propagating components to children. requested
originally for `RenderLayers` but can be useful more generally.

## Solution

- add `HierarchyPropagatePlugin<C, F=()>` which schedules systems to
propagate components through entities matching `F`
- add `Propagate<C: Component + Clone + PartialEq>` which will cause `C`
to be added to all children
more niche features:
- add `PropagateStop<C>` which stops the propagation at this entity
- add `PropagateOver<C>` which allows the propagation to continue to
children, but doesn't add/remove/modify a `C` on this entity itself

## Testing

see tests inline

## Notes

- could happily be an out-of-repo plugin
- not sure where it lives: ideally it would be in `bevy_ecs` but it
requires a `Plugin` so I put it in `bevy_app`, doesn't really belong
there though.
- i'm not totally up-to-date on triggers and observers so possibly this
could be done more cleanly, would be very happy to take review comments
- perf: this is pretty cheap except for `update_reparented` which has to
check the parent of every moved entity. since the entirety is opt-in i
think it's acceptable but i could possibly use `(Changed<Children>,
With<Inherited<C>>)` instead if it's a concern
2025-06-06 00:02:02 +00:00
Carter Anderson
7e9d6d852b
bevyengine.org -> bevy.org (#19503)
We have acquired [bevy.org](https://bevy.org) and the migration has
finished! Meaning we can now update all of the references in this repo.
2025-06-05 23:09:28 +00:00
ZoOL
a35eed0ea4
fix: Ensure linear volume subtraction does not go below zero (#19423)
fix: [Ensure linear volume subtraction does not go below zero
](https://github.com/bevyengine/bevy/issues/19417)

## Solution
- Clamp the result of linear volume subtraction to a minimum of 0.0
- Add a new test case to verify behavior when subtracting beyond zero

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2025-06-05 03:59:20 +00:00
theotherphil
d0f1b3e9f1
Add a few missing doc comments in bevy_image (#19493)
# Objective

Another tiny step towards
https://github.com/bevyengine/bevy/issues/3492.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-04 21:12:02 +00:00
theotherphil
476d79d821
deny(missing_docs) for bevy_state (#19492)
# Objective

Deny missing docs in bevy_state, towards
https://github.com/bevyengine/bevy/issues/3492.
2025-06-04 20:43:47 +00:00
Chris Russell
bd4c960f26
Mention Option and When in the error message for a failing system parameter (#19490)
# Objective

Help users discover how to use `Option<T>` and `When<T>` to handle
failing parameters.

## Solution

Have the error message for a failed parameter mention that `Option<T>`
and `When<T>` can be used to handle the failure.

## Showcase

```
Encountered an error in system `system_name`: Parameter `Res<ResourceType>` failed validation: Resource does not exist
If this is an expected state, wrap the parameter in `Option<T>` and handle `None` when it happens, or wrap the parameter in `When<T>` to skip the system when it happens.
```
2025-06-04 16:39:54 +00:00
Kristoffer Søholm
6fee6fe827
Add get_mut_untracked to Assets (#19487)
# Objective

Fixes #13104

## Solution

Add a `get_mut_untracked` method to `Assets`
2025-06-04 16:34:27 +00:00
theotherphil
c5dcef5e61
deny(missing_docs) for bevy_diagnostic (#19482)
# Objective

Deny missing docs in bevy_diagnostic, towards
https://github.com/bevyengine/bevy/issues/3492.
2025-06-04 01:30:10 +00:00
theotherphil
e7a309ff5f
deny(missing_docs) for bevy_derive (#19483)
# Objective

Deny missing docs in bevy_derive, towards
https://github.com/bevyengine/bevy/issues/3492.
2025-06-04 00:06:32 +00:00
andriyDev
723b52abd3
Allow returning an error from labeled_asset_scope. (#19449)
# Objective

- `LoadContext::labeled_asset_scope` cannot return errors back to the
asset loader. This means users that need errors need to fall back to
using the raw `begin_labeled_asset` and `add_loaded_labeled_asset`,
which is more error-prone.

## Solution

- Allow returning a (generic) error from `labeled_asset_scope`.
- This has the unfortunate side effect that closures which don't return
any errors need to A) return Ok at the end, B) need to specify an error
type (e.g., `()`).

---

## Showcase

```rust
// impl AssetLoader for MyLoader
let handle = load_context.labeled_asset_scope("MySubasset", |mut load_context| {
  if !some_precondition {
    return Err(ThingsDontMakeSenseError);
  }
  let handle = load_context.add_labeled_asset("MySubasset/Other", SomeOtherThing(456));
  Ok(Something{ id: 123, handle })
})?;
```
2025-06-04 00:00:32 +00:00
SpecificProtagonist
5561b40bdf
Use BevyError for AssetLoader::Error (#19478)
# Objective

Allow using `BevyResult` in `AssetLoader`s for consistency. Currently,
it converts errors into `Box<dyn core::error::Error + Send + Sync +
'static>`, which is essentially a `BevyError` without the optional
backtrace functionality.

## Solution

I don't think needs a migration guide as any type that satisfies
`Into<Box<dyn core::error::Error + Send + Sync + 'static>>` also
satisfies `Into<BevyError>`.
2025-06-03 23:58:44 +00:00
François Mockers
7a7bff8c17
Hot patching systems with subsecond (#19309)
# Objective

- Enable hot patching systems with subsecond
- Fixes #19296 

## Solution

- First commit is the naive thin layer
- Second commit only check the jump table when the code is hot patched
instead of on every system execution
- Depends on https://github.com/DioxusLabs/dioxus/pull/4153 for a nicer
API, but could be done without
- Everything in second commit is feature gated, it has no impact when
the feature is not enabled

## Testing

- Check dependencies without the feature enabled: nothing dioxus in tree
- Run the new example: text and color can be changed

---------

Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
2025-06-03 21:12:38 +00:00
re0312
50aa40e980
Trigger ArchetypeCreated event when new archetype is created (#19455)
# Objective

- Part 1 of #19454 .
- Split from PR #18860(authored by @notmd) for better review and limit
implementation impact. so all credit for this work belongs to @notmd .

## Solution

- Trigger `ArchetypeCreated ` when new archetype is createed

---------

Co-authored-by: mgi388 <135186256+mgi388@users.noreply.github.com>
2025-06-02 22:27:45 +00:00
maya
f93e5c5622
Exclude ctrlc from bevy_app for the Nintendo 3DS (#19453)
## Background/motivation

The Nintendo 3DS is supported by the tier 3 rust target
[armv6k-nintendo-3ds](https://doc.rust-lang.org/rustc/platform-support/armv6k-nintendo-3ds.html#armv6k-nintendo-3ds).
Bevy does not officially support the device, but as more of bevy becomes
`no_std` compatible, more targets are being partially supported (e.g.
GBA - https://github.com/bevyengine/bevy/discussions/10680,
https://github.com/bushrat011899/bevy_mod_gba) officially or not.

The Nintendo 3DS runs Horizon as its OS which is
[unix-based](4d08223c05/compiler/rustc_target/src/spec/targets/armv6k_nintendo_3ds.rs (L34)),
and the above target (at least partially) supports rust std. It makes
sense that you would want to use it, since the 3DS supports things like
filesystem reads and the system clock.

## Problem

Unlike standard unix targets, armv6k-nintendo-3ds is not one that can
use/build the the `ctrlc` dependency in `bevy_app` which is enabled by
the bevy `std` cargo feature.

Without the `std` feature flag, scheduled systems panic without
providing another way for bevy to tick using the `Instant` type (like
you might for a
[GBA](72d8bbf47b/src/time.rs (L36))).

<details>

<summary>Example</summary>

```
    Finished `dev` profile [optimized + debuginfo] target(s) in 1m 39s
Building smdh: /home/maya/repos/hyperspace-dj/target/armv6k-nintendo-3ds/debug/hyperspace-dj.smdh
Building 3dsx: /home/maya/repos/hyperspace-dj/target/armv6k-nintendo-3ds/debug/hyperspace-dj.3dsx
Adding RomFS from /home/maya/repos/hyperspace-dj/romfs
Running 3dslink
Sending hyperspace-dj.3dsx, 7172344 bytes
2777346 sent (38.72%), 233 blocks
starting server
server active ...
hii we'are about the to start the bevy app

thread 'main' panicked at /home/maya/repos/bevy/crates/bevy_platform/src/time/fallback.rs:177:13:
An elapsed time getter has not been provided to `Instant`. Please use `Instant::set_elapsed(...)` before calling `Instant::now()`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

</details>

## Solution

This PR simply excludes the `ctrlc` dependency and its uses in
`bevy_app` for the 3DS target (`horizon`) with an addition to its
existing feature flags.

After this fix, we can use the `std` feature, and regular scheduled
systems no longer panic because of missing `Instant` (system clock)
support.

## Testing

I compiled and ran a binary with the modified version of bevy, using
`no_default_features` and feature flags `default_no_std` and `std` on a
physical 3DS (classic) using homebrew and `cargo-3ds`.

Toolchain:
[armv6k-nintendo-3ds](https://doc.rust-lang.org/rustc/platform-support/armv6k-nintendo-3ds.html#armv6k-nintendo-3ds)
(nightly-2025-03-31)
Project reference:
440fc10184

## Considerations

It could be that we don't want to add specific exceptions inside bevy to
support specific hardware with weird quirks inside general bevy code,
but it's not obvious to me what we should use instead of an exception to
(pre-existing) target cfg: every change here is merely an addition to a
cfg that already checks for both the target family and the `std` flag.

It is not clear to me if this PR is exhaustive enough to be considered
an adequate solution for the larger goal of partially supporting the
3DS, but it seems to be a step in the right direction because it at
least lets trivial App::run setups with scheduled systems work.
2025-06-02 22:21:57 +00:00
zacryol
4d4170d834
Implement IntoIterator for Populated and borrows (#19441)
# Objective

`Populated`, a loose wrapper around `Query`, does not implement
`IntoIterator`, requiring either a deref or `into_inner()` call to
access the `Query` and iterate over that.

## Solution

This pr implements `IntoIterator` for `Populated`, `&Populated`, and
`&mut Populated`, each of which forwards the call to the inner `Query`.
This allows the `Populated` to be used directly for any API that takes
an `impl IntoIterator`.

## Testing

`cargo test` was run on the `bevy_ecs` crate
```
test result: ok. 390 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 46.38s
```
2025-06-02 22:19:43 +00:00
AlephCubed
415f6d8ca7
Simplified on_replace and on_despawn relationship hooks. (#19378)
Fixes #18364.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-02 22:15:18 +00:00
theotherphil
3d7486b019
Clarify PartialReflect::apply docs (#19250)
# Objective

Fix https://github.com/bevyengine/bevy/issues/18558

## Solution

* Replace `T` in docs with `Self`
* Fix broken link - replace "introspection subtraits" with "reflection
subtraits"
* Added missing `Set` variant to the list of per-`ReflectKind`-variation
behaviours

## Testing

cargo doc --serve locally to check that the broken link is fixed

---------

Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
2025-06-02 22:12:16 +00:00
JMS55
8255e6cda9
Make TAA non-experimental, fixes (#18349)
The first 4 commits are designed to be reviewed independently.

- Mark TAA non-experimental now that motion vectors are written for
skinned and morphed meshes, along with skyboxes, and add it to
DefaultPlugins
- Adjust halton sequence to match what DLSS is going to use, doesn't
really affect anything, but may as well
- Make MipBias a required component on TAA instead of inserting it in
the render world
- Remove MipBias, TemporalJitter, RenderLayers, etc from the render
world if they're removed from the main world (fixes a retained render
world bug)
- Remove TAA components from the render world properly if
TemporalAntiAliasing is removed from the main world (fixes a retained
render world bug)
- extract_taa_settings() now has to query over `Option<&mut
TemporalAntiAliasing>`, which will match every single camera, in order
to cover cameras that had TemporalAntiAliasing removed this frame. This
kind of sucks, but I can't think of anything better.
- We probably have the same bug with every other rendering feature
component we have.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-06-02 16:04:08 +00:00
Rob Parrett
dc4737923c
Add resize_in_place to Image (#19410)
# Objective

Ultimately, I'd like to modify our font atlas creation systems so that
they are able to resize the font atlases as more glyphs are added. At
the moment, they create a new 512x512 atlas every time one fills up.
With large font sizes and many glyphs, your glyphs may end up spread out
across several atlases.

The goal would be to render text more efficiently, because glyphs spread
across fewer textures could benefit more from batching.

`AtlasAllocator` already has support for growing atlases, but we don't
currently have a way of growing a texture while keeping the pixel data
intact.

## Solution

Add a new method to `Image`: `resize_in_place` and a test for it.

## Testing

Ran the new test, and also a little demo comparing this side-by-side
with `resize`.

<details>
<summary>Expand Code</summary>

```rust
//! Testing ground for #19410

use bevy::prelude::*;
use bevy_render::render_resource::Extent3d;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(Update, test)
        .init_resource::<Size>()
        .insert_resource(FillColor(Hsla::hsl(0.0, 1.0, 0.7)))
        .run();
}

#[derive(Resource, Default)]
struct Size(Option<UVec2>);
#[derive(Resource)]
struct FillColor(Hsla);
#[derive(Component)]
struct InPlace;

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2d);

    commands.spawn((
        Transform::from_xyz(220.0, 0.0, 0.0),
        Sprite::from_image(asset_server.load("branding/bevy_bird_dark.png")),
    ));

    commands.spawn((
        InPlace,
        Transform::from_xyz(-220.0, 0.0, 0.0),
        Sprite::from_image(asset_server.load("branding/icon.png")),
    ));
}

fn test(
    sprites: Query<(&Sprite, Has<InPlace>)>,
    mut images: ResMut<Assets<Image>>,
    mut new_size: ResMut<Size>,
    mut dir: Local<IVec2>,
    mut color: ResMut<FillColor>,
) -> Result {
    for (sprite, in_place) in &sprites {
        let image = images.get_mut(&sprite.image).ok_or("Image not found")?;
        let size = new_size.0.get_or_insert(image.size());

        if *dir == IVec2::ZERO {
            *dir = IVec2::splat(1);
        }

        *size = size.saturating_add_signed(*dir);

        if size.x > 400 || size.x < 150 {
            *dir = *dir * -1;
        }

        color.0 = color.0.rotate_hue(1.0);

        if in_place {
            image.resize_in_place_2d(
                Extent3d {
                    width: size.x,
                    height: size.y,
                    ..default()
                },
                &Srgba::from(color.0).to_u8_array(),
            )?;
        } else {
            image.resize(Extent3d {
                width: size.x,
                height: size.y,
                ..default()
            });
        }
    }

    Ok(())
}
```
</details>


https://github.com/user-attachments/assets/6b2d0ec3-6a6e-4da1-98aa-29e7162f16fa

## Alternatives

I think that this might be useful functionality outside of the font
atlas scenario, but we *could* just increase the initial font atlas
size, make it configurable, and/or size font atlases according to device
limits. It's not totally clear to me how to accomplish that last idea.
2025-05-31 21:55:11 +00:00
AlephCubed
b993202d79
Refactor state scoped events to match entities. (#19435)
This adds support for clearing events when **entering** a state (instead
of just when exiting) and updates the names to match
`DespawnOnExitState`.

Before:
```rust
app.add_state_scoped_event::<MyGameEvent>(GameState::Play);
```
After:
```rust
app
  .add_event::<MyGameEvent>()
  .clear_events_on_exit_state::<MyGameEvent>(GameState::Play);
```
2025-05-31 20:14:14 +00:00
Eagster
61bd3af7c7
Remove invalid entity locations (#19433)
# Objective

This is the first step of #19430 and is a follow up for #19132.

Now that `ArchetypeRow` has a niche, we can use `Option` instead of
needing `INVALID` everywhere.

This was especially concerning since `INVALID` *really was valid!*

Using options here made the code clearer and more data-driven. 

## Solution

Replace all uses of `INVALID` entity locations (and archetype/table
rows) with `None`.

## Testing

CI

---------

Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-31 16:34:33 +00:00
ickshonpe
43b8fbda93
Unrequire VisibilityClass from Node (#17918)
# Objective

The UI doesn't use `ViewVisibility` so it doesn't do anything.

## Solution

Remove it.
2025-05-31 08:18:01 +00:00
Rob Parrett
0fca6938ea
Fix missing meta files breaking Bevy on itch (#19268)
# Objective

Fixes #19029 (also maybe sorta #18002, but we may want to handle the SPA
issue I outlined there more gracefully?)


## Solution

The most minimal / surgical approach I could think of, hopefully
cherry-pickable for a point release.

It seems that it's not *entirely* crazy for web services to return 403
for an item that was not found. Here's an example from [Amazon
CloudFront
docs](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/http-403-permission-denied.html#s3-origin-403-error).
If it is somewhat common for web services to behave this way, then I
think it's best to also treat these responses as if they were "not
found."

I was previously of the opinion that any 400 level error "might as well"
get this treatment, but I'm now thinking that's probably overkill and
there are quite a few 400 level statuses that would indicate some
problem that needs to be fixed, and interpreting these as "not found"
might add confusion to the debugging process.

## Testing

Tested this with a web server that returns 403 for requests to meta
files.

```bash
cargo run -p build-wasm-example -- --api webgl2 sprite && \
open "http://localhost:4000" && \
python3 test_403.py examples/wasm
```
`test_403.py`:
```python
from http.server import HTTPServer, SimpleHTTPRequestHandler
import os
import sys


class CustomHandler(SimpleHTTPRequestHandler):
    def do_GET(self):
        if self.path.endswith(".meta"):
            self.send_response(403)
            self.send_header("Content-type", "text/plain")
            self.end_headers()
            self.wfile.write(b"403 Forbidden: Testing.\n")
        else:
            super().do_GET()


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} <directory>")
        sys.exit(1)

    os.chdir(sys.argv[1])

    server_address = ("", 4000)
    httpd = HTTPServer(server_address, CustomHandler)
    httpd.serve_forever()
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-05-30 20:33:47 +00:00
Sarthak Singh
d82d3238d1
Fixed memory leak in bindless material (#19041)
# Objective

Fixed #19035. Fixed #18882. It consisted of two different bugs:
- The allocations where being incremented even when a Data binding was
created.
- The ref counting on the binding was broken.

## Solution

- Stopped incrementing the allocations when a data binding was created.
- Rewrote the ref counting code to more reliably track the ref count.

## Testing

Tested my fix for 10 minutes with the `examples/3d/animated_material.rs`
example. I changed the example to spawn 51x51 meshes instead of 3x3
meshes to heighten the effects of the bug.

My branch: (After 10 minutes of running the modified example)
GPU: 172 MB
CPU: ~700 MB

Main branch: (After 2 minutes of running the modified example, my
computer started to stutter so I had to end it early)
GPU: 376 MB
CPU: ~1300 MB
2025-05-30 19:36:56 +00:00
robtfm
c617fc49ae
fix distinct directional lights per view (#19147)
# Objective

after #15156 it seems like using distinct directional lights on
different views is broken (and will probably break spotlights too). fix
them

## Solution

the reason is a bit hairy so with an example:

- camera 0 on layer 0
- camera 1 on layer 1
- dir light 0 on layer 0 (2 cascades)
- dir light 1 on layer 1 (2 cascades)

in render/lights.rs:
- outside of any view loop, 
- we count the total number of shadow casting directional light cascades
(4) and assign an incrementing `depth_texture_base_index` for each (0-1
for one light, 2-3 for the other, depending on iteration order) (line
1034)
- allocate a texture array for the total number of cascades plus
spotlight maps (4) (line 1106)

- in the view loop, for directional lights we 
  - skip lights that don't intersect on renderlayers (line 1440)
- assign an incrementing texture layer to each light/cascade starting
from 0 (resets to 0 per view) (assigning 0 and 1 each time for the 2
cascades of the intersecting light) (line 1509, init at 1421)

then in the rendergraph:
- camera 0 renders the shadow map for light 0 to texture indices 0 and 1
- camera 0 renders using shadows from the `depth_texture_base_index`
(maybe 0-1, maybe 2-3 depending on the iteration order)

- camera 1 renders the shadow map for light 1 to texture indices 0 and 1
- camera 0 renders using shadows from the `depth_texture_base_index`
(maybe 0-1, maybe 2-3 depending on the iteration order)

issues:
- one of the views uses empty shadow maps (bug)
- we allocated a texture layer per cascade per light, even though not
all lights are used on all views (just inefficient)
- I think we're allocating texture layers even for lights with
`shadows_enabled: false` (just inefficient)

solution:
- calculate upfront the view with the largest number of directional
cascades
- allocate this many layers (plus layers for spotlights) in the texture
array
- keep using texture layers 0..n in the per-view loop, but build
GpuLights.gpu_directional_lights within the loop too so it refers to the
same layers we render to

nice side effects: 
- we can now use `max_texture_array_layers / MAX_CASCADES_PER_LIGHT`
shadow-casting directional lights per view, rather than overall.
- we can remove the `GpuDirectionalLight::skip` field, since the gpu
lights struct is constructed per view

a simpler approach would be to keep everything the same, and just
increment the texture layer index in the view loop even for
non-intersecting lights. this pr reduces the total shadowmap vram used
as well and isn't *much* extra complexity. but if we want something less
risky/intrusive for 16.1 that would be the way.

## Testing

i edited the split screen example to put separate lights on layer 1 and
layer 2, and put the plane and fox on both layers (using lots of
unrelated code for render layer propagation from #17575).
without the fix the directional shadows will only render on one of the
top 2 views even though there are directional lights on both layers.

```rs
//! Renders two cameras to the same window to accomplish "split screen".

use std::f32::consts::PI;

use bevy::{
    pbr::CascadeShadowConfigBuilder, prelude::*, render:📷:Viewport, window::WindowResized,
};
use bevy_render::view::RenderLayers;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(HierarchyPropagatePlugin::<RenderLayers>::default())
        .add_systems(Startup, setup)
        .add_systems(Update, (set_camera_viewports, button_system))
        .run();
}

/// set up a simple 3D scene
fn setup(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    let all_layers = RenderLayers::layer(1).with(2).with(3).with(4);

    // plane
    commands.spawn((
        Mesh3d(meshes.add(Plane3d::default().mesh().size(100.0, 100.0))),
        MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
        all_layers.clone()
    ));

    commands.spawn((
        SceneRoot(
            asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")),
        ),
        Propagate(all_layers.clone()),
    ));

    // Light
    commands.spawn((
        Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
        DirectionalLight {
            shadows_enabled: true,
            ..default()
        },
        CascadeShadowConfigBuilder {
            num_cascades: if cfg!(all(
                feature = "webgl2",
                target_arch = "wasm32",
                not(feature = "webgpu")
            )) {
                // Limited to 1 cascade in WebGL
                1
            } else {
                2
            },
            first_cascade_far_bound: 200.0,
            maximum_distance: 280.0,
            ..default()
        }
        .build(),
        RenderLayers::layer(1),
    ));

    commands.spawn((
        Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
        DirectionalLight {
            shadows_enabled: true,
            ..default()
        },
        CascadeShadowConfigBuilder {
            num_cascades: if cfg!(all(
                feature = "webgl2",
                target_arch = "wasm32",
                not(feature = "webgpu")
            )) {
                // Limited to 1 cascade in WebGL
                1
            } else {
                2
            },
            first_cascade_far_bound: 200.0,
            maximum_distance: 280.0,
            ..default()
        }
        .build(),
        RenderLayers::layer(2),
    ));

    // Cameras and their dedicated UI
    for (index, (camera_name, camera_pos)) in [
        ("Player 1", Vec3::new(0.0, 200.0, -150.0)),
        ("Player 2", Vec3::new(150.0, 150., 50.0)),
        ("Player 3", Vec3::new(100.0, 150., -150.0)),
        ("Player 4", Vec3::new(-100.0, 80., 150.0)),
    ]
    .iter()
    .enumerate()
    {
        let camera = commands
            .spawn((
                Camera3d::default(),
                Transform::from_translation(*camera_pos).looking_at(Vec3::ZERO, Vec3::Y),
                Camera {
                    // Renders cameras with different priorities to prevent ambiguities
                    order: index as isize,
                    ..default()
                },
                CameraPosition {
                    pos: UVec2::new((index % 2) as u32, (index / 2) as u32),
                },
                RenderLayers::layer(index+1)
            ))
            .id();

        // Set up UI
        commands
            .spawn((
                UiTargetCamera(camera),
                Node {
                    width: Val::Percent(100.),
                    height: Val::Percent(100.),
                    ..default()
                },
            ))
            .with_children(|parent| {
                parent.spawn((
                    Text::new(*camera_name),
                    Node {
                        position_type: PositionType::Absolute,
                        top: Val::Px(12.),
                        left: Val::Px(12.),
                        ..default()
                    },
                ));
                buttons_panel(parent);
            });
    }

    fn buttons_panel(parent: &mut ChildSpawnerCommands) {
        parent
            .spawn(Node {
                position_type: PositionType::Absolute,
                width: Val::Percent(100.),
                height: Val::Percent(100.),
                display: Display::Flex,
                flex_direction: FlexDirection::Row,
                justify_content: JustifyContent::SpaceBetween,
                align_items: AlignItems::Center,
                padding: UiRect::all(Val::Px(20.)),
                ..default()
            })
            .with_children(|parent| {
                rotate_button(parent, "<", Direction::Left);
                rotate_button(parent, ">", Direction::Right);
            });
    }

    fn rotate_button(parent: &mut ChildSpawnerCommands, caption: &str, direction: Direction) {
        parent
            .spawn((
                RotateCamera(direction),
                Button,
                Node {
                    width: Val::Px(40.),
                    height: Val::Px(40.),
                    border: UiRect::all(Val::Px(2.)),
                    justify_content: JustifyContent::Center,
                    align_items: AlignItems::Center,
                    ..default()
                },
                BorderColor(Color::WHITE),
                BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
            ))
            .with_children(|parent| {
                parent.spawn(Text::new(caption));
            });
    }
}

#[derive(Component)]
struct CameraPosition {
    pos: UVec2,
}

#[derive(Component)]
struct RotateCamera(Direction);

enum Direction {
    Left,
    Right,
}

fn set_camera_viewports(
    windows: Query<&Window>,
    mut resize_events: EventReader<WindowResized>,
    mut query: Query<(&CameraPosition, &mut Camera)>,
) {
    // We need to dynamically resize the camera's viewports whenever the window size changes
    // so then each camera always takes up half the screen.
    // A resize_event is sent when the window is first created, allowing us to reuse this system for initial setup.
    for resize_event in resize_events.read() {
        let window = windows.get(resize_event.window).unwrap();
        let size = window.physical_size() / 2;

        for (camera_position, mut camera) in &mut query {
            camera.viewport = Some(Viewport {
                physical_position: camera_position.pos * size,
                physical_size: size,
                ..default()
            });
        }
    }
}

fn button_system(
    interaction_query: Query<
        (&Interaction, &ComputedNodeTarget, &RotateCamera),
        (Changed<Interaction>, With<Button>),
    >,
    mut camera_query: Query<&mut Transform, With<Camera>>,
) {
    for (interaction, computed_target, RotateCamera(direction)) in &interaction_query {
        if let Interaction::Pressed = *interaction {
            // Since TargetCamera propagates to the children, we can use it to find
            // which side of the screen the button is on.
            if let Some(mut camera_transform) = computed_target
                .camera()
                .and_then(|camera| camera_query.get_mut(camera).ok())
            {
                let angle = match direction {
                    Direction::Left => -0.1,
                    Direction::Right => 0.1,
                };
                camera_transform.rotate_around(Vec3::ZERO, Quat::from_axis_angle(Vec3::Y, angle));
            }
        }
    }
}








use std::marker::PhantomData;

use bevy::{
    app::{App, Plugin, Update},
    ecs::query::QueryFilter,
    prelude::{
        Changed, Children, Commands, Component, Entity, Local, Query,
        RemovedComponents, SystemSet, With, Without,
    },
};

/// Causes the inner component to be added to this entity and all children.
/// A child with a Propagate<C> component of it's own will override propagation from
/// that point in the tree
#[derive(Component, Clone, PartialEq)]
pub struct Propagate<C: Component + Clone + PartialEq>(pub C);

/// Internal struct for managing propagation
#[derive(Component, Clone, PartialEq)]
pub struct Inherited<C: Component + Clone + PartialEq>(pub C);

/// Stops the output component being added to this entity.
/// Children will still inherit the component from this entity or its parents
#[derive(Component, Default)]
pub struct PropagateOver<C: Component + Clone + PartialEq>(PhantomData<fn() -> C>);

/// Stops the propagation at this entity. Children will not inherit the component.
#[derive(Component, Default)]
pub struct PropagateStop<C: Component + Clone + PartialEq>(PhantomData<fn() -> C>);

pub struct HierarchyPropagatePlugin<C: Component + Clone + PartialEq, F: QueryFilter = ()> {
    _p: PhantomData<fn() -> (C, F)>,
}

impl<C: Component + Clone + PartialEq, F: QueryFilter> Default for HierarchyPropagatePlugin<C, F> {
    fn default() -> Self {
        Self {
            _p: Default::default(),
        }
    }
}

#[derive(SystemSet, Clone, PartialEq, PartialOrd, Ord)]
pub struct PropagateSet<C: Component + Clone + PartialEq> {
    _p: PhantomData<fn() -> C>,
}

impl<C: Component + Clone + PartialEq> std::fmt::Debug for PropagateSet<C> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("PropagateSet")
            .field("_p", &self._p)
            .finish()
    }
}

impl<C: Component + Clone + PartialEq> Eq for PropagateSet<C> {}
impl<C: Component + Clone + PartialEq> std:#️⃣:Hash for PropagateSet<C> {
    fn hash<H: std:#️⃣:Hasher>(&self, state: &mut H) {
        self._p.hash(state);
    }
}

impl<C: Component + Clone + PartialEq> Default for PropagateSet<C> {
    fn default() -> Self {
        Self {
            _p: Default::default(),
        }
    }
}

impl<C: Component + Clone + PartialEq, F: QueryFilter + 'static> Plugin
    for HierarchyPropagatePlugin<C, F>
{
    fn build(&self, app: &mut App) {
        app.add_systems(
            Update,
            (
                update_source::<C, F>,
                update_stopped::<C, F>,
                update_reparented::<C, F>,
                propagate_inherited::<C, F>,
                propagate_output::<C, F>,
            )
                .chain()
                .in_set(PropagateSet::<C>::default()),
        );
    }
}

pub fn update_source<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    changed: Query<(Entity, &Propagate<C>), (Changed<Propagate<C>>, Without<PropagateStop<C>>)>,
    mut removed: RemovedComponents<Propagate<C>>,
) {
    for (entity, source) in &changed {
        commands
            .entity(entity)
            .try_insert(Inherited(source.0.clone()));
    }

    for removed in removed.read() {
        if let Ok(mut commands) = commands.get_entity(removed) {
            commands.remove::<(Inherited<C>, C)>();
        }
    }
}

pub fn update_stopped<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    q: Query<Entity, (With<Inherited<C>>, F, With<PropagateStop<C>>)>,
) {
    for entity in q.iter() {
        let mut cmds = commands.entity(entity);
        cmds.remove::<Inherited<C>>();
    }
}

pub fn update_reparented<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    moved: Query<
        (Entity, &ChildOf, Option<&Inherited<C>>),
        (
            Changed<ChildOf>,
            Without<Propagate<C>>,
            Without<PropagateStop<C>>,
            F,
        ),
    >,
    parents: Query<&Inherited<C>>,
) {
    for (entity, parent, maybe_inherited) in &moved {
        if let Ok(inherited) = parents.get(parent.parent()) {
            commands.entity(entity).try_insert(inherited.clone());
        } else if maybe_inherited.is_some() {
            commands.entity(entity).remove::<(Inherited<C>, C)>();
        }
    }
}

pub fn propagate_inherited<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    changed: Query<
        (&Inherited<C>, &Children),
        (Changed<Inherited<C>>, Without<PropagateStop<C>>, F),
    >,
    recurse: Query<
        (Option<&Children>, Option<&Inherited<C>>),
        (Without<Propagate<C>>, Without<PropagateStop<C>>, F),
    >,
    mut to_process: Local<Vec<(Entity, Option<Inherited<C>>)>>,
    mut removed: RemovedComponents<Inherited<C>>,
) {
    // gather changed
    for (inherited, children) in &changed {
        to_process.extend(
            children
                .iter()
                .map(|child| (child, Some(inherited.clone()))),
        );
    }

    // and removed
    for entity in removed.read() {
        if let Ok((Some(children), _)) = recurse.get(entity) {
            to_process.extend(children.iter().map(|child| (child, None)))
        }
    }

    // propagate
    while let Some((entity, maybe_inherited)) = (*to_process).pop() {
        let Ok((maybe_children, maybe_current)) = recurse.get(entity) else {
            continue;
        };

        if maybe_current == maybe_inherited.as_ref() {
            continue;
        }

        if let Some(children) = maybe_children {
            to_process.extend(
                children
                    .iter()
                    .map(|child| (child, maybe_inherited.clone())),
            );
        }

        if let Some(inherited) = maybe_inherited {
            commands.entity(entity).try_insert(inherited.clone());
        } else {
            commands.entity(entity).remove::<(Inherited<C>, C)>();
        }
    }
}

pub fn propagate_output<C: Component + Clone + PartialEq, F: QueryFilter>(
    mut commands: Commands,
    changed: Query<
        (Entity, &Inherited<C>, Option<&C>),
        (Changed<Inherited<C>>, Without<PropagateOver<C>>, F),
    >,
) {
    for (entity, inherited, maybe_current) in &changed {
        if maybe_current.is_some_and(|c| &inherited.0 == c) {
            continue;
        }

        commands.entity(entity).try_insert(inherited.0.clone());
    }
}
```
2025-05-30 19:35:55 +00:00
Manuel Brea Carreras
3902804114
Fix #19219 by moving observer triggers out of resource_scope (#19221)
# Objective

Fixes #19219 

## Solution

Instead of calling `world.commands().trigger` and
`world.commands().trigger_targets` whenever each scene is spawned, save
the `instance_id` and optional parent entity to perform all such calls
at the end. This prevents the potential flush of the world command queue
that can happen if `add_child` is called from causing the crash.

## Testing

- Did you test these changes? If so, how?
- Verified that I can no longer reproduce the bug with the instructions
at #19219.
  - Ran `bevy_scene` tests
- Visually verified that the following examples still run as expected
`many_foxes`, `scene` . (should I test any more?)
- Are there any parts that need more testing?
- Pending to run `cargo test` at the root to test that all examples
still build; I will update the PR when that's done
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - Run bevy as usual
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - N/a (tested on Linux/wayland but it shouldn't be relevant)

---
2025-05-30 19:33:47 +00:00
eugineerd
0694743d8d
Fix EntityCloner replacing required components. (#19326)
# Objective
Fix #19324

## Solution
`EntityCloner` replaces required components when filtering. This is
unexpected when comparing with the way the rest of bevy handles required
components. This PR separates required components from explicit
components when filtering in `EntityClonerBuilder`.

## Testing
Added a regression test for this case.
2025-05-30 19:28:53 +00:00
theotherphil
0ee8bf978c
Clarify Resource change detection behaviour in condition docs (#19252)
# Objective

Fixes https://github.com/bevyengine/bevy/issues/17933

## Solution

Correct "value has changed'" in docs to "value has been added or mutably
dereferenced", with a note for emphasis copied from the docs for
Changed.

## Testing

-
2025-05-30 00:47:25 +00:00
AlephCubed
6c003ef794
Loosen add_state_scoped_event trait bound. (#19401)
Fixes #18623, allowing `add_state_scoped_event` to work with computed
states.

As a side note, should state scoped events be updated to match the
recently changed [state scoped
entities](https://github.com/bevyengine/bevy/blob/main/release-content/migration-guides/rename_StateScoped.md)?
2025-05-29 21:37:53 +00:00
Coty
439c0fb973
fix reference in example usage comments (#19434)
# Objective

- Fix a reference in the example usage comments of bevy_utils
Default::default

## Solution

- Just a word change in the comments
 
## Testing

- No actual code changes to test
2025-05-29 19:12:55 +00:00
L. Borella (Villi)
59ef10562a
Allow restricting audio playback to a custom region (#19400)
# Objective

Adds the ability to restrict playback of an audio source to a certain
region in time. In other words, you can set a custom start position and
duration for the audio clip. These options are set via the
`PlaybackSettings` component, and it works on all kinds of audio
sources.

## Solution

- Added public `start_position` and `duration` fields to
`PlaybackSettings`, both of type `Option<std::time::Duration>`.
- Used rodio's `Source::skip_duration` and `Source::take_duration`
functions to implement start position and duration, respectively.
- If the audio is looping, it interacts as you might expect - the loop
will start at the start position and end after the duration.
- If the start position is None (the default value), the audio will
start from the beginning, like normal. Similarly, if the duration is
None (default), the audio source will play for as long as possible.

## Testing

I tried adding a custom start position to all the existing audio
examples to test a bunch of different audio sources and settings, and
they all worked fine. I verified that it skips the right amount of time,
and that it skips the entire audio clip if the start position is longer
than the length of the clip. All my testing was done on Fedora Linux.

Update: I did similar testing for duration, and ensured that the two
options worked together in combination and interacted well with looping
audio.

---

## Showcase

```rust
// Play a 10 second segment of a song, starting at 0:30.5
commands.spawn((
    AudioPlayer::new(song_handle),
    PlaybackSettings::LOOP
        .with_start_position(Duration::from_secs_f32(30.5))
        .with_duration(Duration::from_secs(10))
));
```
2025-05-29 18:45:37 +00:00
andristarr
fe678e1eeb
Updating mesh_picking doc to include RenderAssetUsages (#19413)
# Objective
Fixes #19102 

Updating mesh_picking doc stating that MAIN_WORLD RenderAssetUsages
needs to be on the mesh to be picked.
2025-05-29 18:44:50 +00:00
Daniel Skates
922ee480d2
Rename Position to UiPosition in bevy_ui (#19422)
# Objective

- Fixes #19418

## Solution

- Rename Position to UiPosition in bevy_ui

## Testing

- `cargo build`
- `cargo run --example gradients`
- `cargo run --example stacked_gradients`
2025-05-29 14:52:44 +00:00
Eagster
1966e44400
EntityGeneration ordering (#19421)
# Objective

Recently the `u32` `Entity::generation` was replaced with the new
`EntityGeneration` in #19121.
This made meanings a lot more clear, and prevented accidental misuse.

One common misuse was assuming that `u32`s that were greater than others
came after those others.
Wrapping makes this assumption false.
When `EntityGeneration` was created, it retained the `u32` ordering,
which was useless at best and wrong at worst.
This pr fixes the ordering implementation, so new generations are
greater than older generations.

Some users were already accounting for this ordering issue (which was
still present in 0.16 and before) by manually accessing the `u32`
representation. This made migrating difficult for avian physics; see
[here](https://discord.com/channels/691052431525675048/749335865876021248/1377431569228103780).

I am generally of the opinion that this type should be kept opaque to
prevent accidental misuse.
As we find issues like this, the functionality should be added to
`EntityGeneration` directly.

## Solution

Fix the ordering implementation through `Ord`.

Alternatively, we could keep `Ord` the same and make a `cmp_age` method,
but I think this is better, even though sorting entity ids may be
*marginally* slower now (but more correct). This is a tradeoff.

## Testing

I improved documentation for aliasing and ordering, adding some doc
tests.
2025-05-29 05:48:32 +00:00
SpecificProtagonist
a266e7e642
More uninlined_format_args fixes (#19396)
# Objective

There are several uninlined format args (seems to be in more formatting
macros and in more crates) that are not detected on stable, but are on
nightly.

## Solution

Fix them.
2025-05-28 02:35:18 +00:00
SpecificProtagonist
13e89a1678
Fix EntityMeta.spawned_or_despawned unsoundness (#19350)
# Objective

#19047 added an `MaybeUninit` field to `EntityMeta`, but did not
guarantee that it will be initialized before access:

```rust
let mut world = World::new();
let id = world.entities().reserve_entity();
world.flush();
world.entity(id);
```

<details>
<summary>Miri Error</summary>

```
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
    --> /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1121:26
     |
1121 |                 unsafe { meta.spawned_or_despawned.assume_init() }
     |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
     |
     = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
     = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
     = note: BACKTRACE:
     = note: inside closure at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1121:26: 1121:65
     = note: inside `std::option::Option::<&bevy_ecs::entity::EntityMeta>::map::<bevy_ecs::entity::SpawnedOrDespawned, {closure@bevy_ecs::entity::Entities::entity_get_spawned_or_despawned::{closure#1}}>` at /home/vj/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:1144:29: 1144:33
     = note: inside `bevy_ecs::entity::Entities::entity_get_spawned_or_despawned` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1112:9: 1122:15
     = note: inside closure at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1094:13: 1094:57
     = note: inside `bevy_ecs::change_detection::MaybeLocation::<std::option::Option<&std::panic::Location<'_>>>::new_with_flattened::<{closure@bevy_ecs::entity::Entities::entity_get_spawned_or_despawned_by::{closure#0}}>` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/change_detection.rs:1371:20: 1371:24
     = note: inside `bevy_ecs::entity::Entities::entity_get_spawned_or_despawned_by` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1093:9: 1096:11
     = note: inside `bevy_ecs::entity::Entities::entity_does_not_exist_error_details` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1163:23: 1163:70
     = note: inside `bevy_ecs::entity::EntityDoesNotExistError::new` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/entity/mod.rs:1182:22: 1182:74
     = note: inside `bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell::<'_>::get_entity` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/unsafe_world_cell.rs:368:20: 368:73
     = note: inside `<bevy_ecs::entity::Entity as bevy_ecs::world::WorldEntityFetch>::fetch_ref` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/entity_fetch.rs:207:21: 207:42
     = note: inside `bevy_ecs::world::World::get_entity::<bevy_ecs::entity::Entity>` at /home/vj/workspace/rust/bevy/crates/bevy_ecs/src/world/mod.rs:911:18: 911:42
note: inside `main`
    --> src/main.rs:12:15
     |
12   |     world.entity(id);
     |
```

</details>

## Solution

- remove the existing `MaybeUninit` in `EntityMeta.spawned_or_despawned`
- initialize during flush. This is not needed for soundness, but not
doing this means we can't return a sensible location/tick for flushed
entities.

## Testing

Test via the snippet above (also added equivalent test).

---------

Co-authored-by: urben1680 <55257931+urben1680@users.noreply.github.com>
2025-05-27 22:45:07 +00:00
andriyDev
b866bb4254
Remove Shader weak_handles from bevy_pbr (excluding meshlets). (#19365)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_pbr` (excluding meshlets).

## Testing

- `atmosphere` example still works
- `fog` example still works
- `decal` example still works

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:47 +00:00
andriyDev
e8c2a5af66
Remove Shader weak_handles from bevy_ui. (#19393)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_ui`.

## Testing

- `box_shadow` example still works.
- `gradient` example is broken at head (see #19384) - but otherwise
gives the same result in the console.
- `ui_materials` example still works.
- `ui_texture_slice` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:40 +00:00
andriyDev
846cec9ed2
Remove Shader weak_handles from bevy_gizmos. (#19394)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_gizmos`.

## Testing

- `2d_gizmos` example still works.
- `3d_gizmos` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:32 +00:00
andriyDev
57588eb7eb
Remove Shader weak_handles from bevy_core_pipeline (except two). (#19395)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_core_pipeline`.

## Testing

- `bloom_3d` example still works.
- `motion_blur` example still works.
- `meshlet` example still works (it uses a shader from core).

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 22:32:27 +00:00
Chris Berger
a8376e982e
Rename Timer::finished and Timer::paused to is_finished and is_paused (#19386)
# Objective
Renames `Timer::finished` and `Timer::paused` to `Timer::is_finished`
and `Timer::is_paused` to align the public APIs for `Time`, `Timer`, and
`Stopwatch`.

Fixes #19110
2025-05-27 22:24:18 +00:00
Rob Parrett
e0ed28022d
Fix error in gradient shader (#19384)
# Objective

Fixes #19383

## Solution

Add missing param and flags from `ui.wgsl` to `gradients.wgsl`

## Testing

`cargo run --example gradients`
`cargo run --example stacked_gradients`
`cargo run --example radial_gradients`

## Notes

`radial_gradients` looks broken, but this appears to be a separate
issue. Its appearance now is the same as in the [first
screenshot](https://pixel-eagle.com/project/b25a040a-a980-4602-b90c-d480ab84076d/run/10348/compare/10342?screenshot=UI%20(User%20Interface)/radial_gradients.png)
recorded in the example runner.

I will document this in a separate issue.
2025-05-27 21:29:10 +00:00
HeartofPhos
131f99de23
Fix custom relations panics with parent/child relations (#19341)
# Objective

Fixes #18905

## Solution

`world.commands().entity(target_entity).queue(command)` calls
`commands.with_entity` without an error handler, instead queue on
`Commands` with an error handler

## Testing

Added unit test

Co-authored-by: Heart <>
2025-05-27 21:05:31 +00:00
Griffin
e981bb8902
Make light cascades and tonemapping luts pub (#19189)
Make directional light cascades and tonemapping luts pub so that custom
render passes / backends can use them.
2025-05-27 19:46:04 +00:00
Chris Russell
571b3ba475
Remove ArchetypeComponentId and archetype_component_access (#19143)
# Objective

Remove `ArchetypeComponentId` and `archetype_component_access`.
Following #16885, they are no longer used by the engine, so we can stop
spending time calculating them or space storing them.

## Solution

Remove `ArchetypeComponentId` and everything that touches it.  

The `System::update_archetype_component_access` method no longer needs
to update `archetype_component_access`. We do still need to update query
caches, but we no longer need to do so *before* running the system. We'd
have to touch every caller anyway if we gave the method a better name,
so just remove `System::update_archetype_component_access` and
`SystemParam::new_archetype` entirely, and update the query cache in
`Query::get_param`.

The `Single` and `Populated` params also need their query caches updated
in `SystemParam::validate_param`, so change `validate_param` to take
`&mut Self::State` instead of `&Self::State`.
2025-05-27 19:04:32 +00:00
atlv
2946de4573
doc(render): fix incorrectly transposed view matrix docs (#19317)
# Objective

- Mend incorrect docs

## Solution

- Mend them
- add example use
- clarify column major

## Testing

- No code changes
2025-05-27 04:58:58 +00:00
atlv
d4985af7cb
refactor(utils): move SyncCell and SyncUnsafeCell to bevy_platform (#19305)
# Objective

- move SyncCell and SyncUnsafeCell to bevy_platform

## Solution

- move SyncCell and SyncUnsafeCell to bevy_platform

## Testing

- cargo clippy works
2025-05-27 04:57:26 +00:00
andriyDev
dc6563477d
Remove Shader weak_handles from bevy_sprite. (#19392)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_sprite`.

## Testing

- `sprite` example still works.
- `mesh2d` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 04:01:34 +00:00
atlv
1732c2253b
refactor(render): move WgpuWrapper into bevy_utils (#19303)
# Objective

- A step towards splitting out bevy_camera from bevy_render

## Solution

- Move a shim type into bevy_utils to avoid a dependency cycle
- Manually expand Deref/DerefMut to avoid having a bevy_derive
dependency so early in the dep tree

## Testing

- It compiles
2025-05-27 03:43:49 +00:00
AlephCubed
7d32dfec18
Add insert_if_new test for sparse set. (#19387)
Fixes #19081.
Simply created a duplicate of the existing `insert_if_new` test, but
using sparse sets.

## Testing:
The test passes on main, but fails if #19059 is reverted.
2025-05-27 03:15:30 +00:00
andriyDev
33b3c08515
Remove Shader weak_handles from bevy_anti_aliasing. (#19391)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
`bevy_anti_aliasing`.

## Testing

- `anti_aliasing` example still works.

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-27 03:14:55 +00:00
Rob Parrett
0ff44c9493
Small fixes for gradient docs (#19388)
# Objective

Found a typo while looking at gradients in another issue and gave the
docs a skim for more.

## Solution

A couple typo fixes and some tiny improvements
2025-05-26 23:20:24 +00:00
theotherphil
1e1853b876
Add missing doc comments for log_diagnostics_plugin (#19261)
# Objective

Fill some of the missing docs from bevy_diagnostics.
2025-05-26 22:29:59 +00:00
ickshonpe
f415e2e96d
Constify Val::resolve and BorderRadius::resolve (#18595)
# Objective

Constify `Val::resolve` and  `BorderRadius::resolve`

# Solution

* Replace uses of `Vec2::min_element` and `Vec2::max_element` with `min`
and `max` called on the components.
* Make `BorderRadius::resolve` and `BorderRadius::resolve_single_corner`
`const`.
* Swap the order of the `bottom_left` and `bottom_right` fields of
`BorderRadius` and `ResolvedBorderRadius` so they match the ccw order
used in the shader and in css.
2025-05-26 22:17:52 +00:00
theotherphil
523600133d
Add missing docs for diagnostics.rs (#19264)
# Objective

Fill in some more missing doc comments.

---------

Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2025-05-26 20:27:46 +00:00
theotherphil
16a286dac3
Update .entry() docs to show both insert-then-modify and modify-or-insert examples (#19327)
# Objective

Fix https://github.com/bevyengine/bevy/issues/16379
2025-05-26 20:27:20 +00:00
François Mockers
8a223be651
Enable state scoped entities by default (#19354)
# Objective

- Enable state scoped entities by default
- Provide a way to disable it when needed

---------

Co-authored-by: Ben Frankel <ben.frankel7@gmail.com>
2025-05-26 20:26:41 +00:00
andriyDev
8db7b6e122
Remove Shader weak_handles from bevy_render. (#19362)
# Objective

- Related to #19024

## Solution

- Use the new `load_shader_library` macro for the shader libraries and
`embedded_asset`/`load_embedded_asset` for the "shader binaries" in
bevy_render.

## Testing

- `animate_shader` example still works

P.S. I don't think this needs a migration guide. Technically users could
be using the `pub` weak handles, but there's no actual good use for
them, so omitting it seems fine. Alternatively, we could mix this in
with the migration guide notes for #19137.
2025-05-26 20:20:25 +00:00
SpecificProtagonist
158d9aff0e
Fix spawn tracking for spawn commands (#19351)
# Objective

See also
https://discord.com/channels/691052431525675048/1374187654425481266/1375553989185372292.

## Solution

Set spawn info in `Commands::spawn_empty`.
Also added a benchmark for `Commands::spawn`.

## Testing

See added test.
2025-05-26 20:15:21 +00:00
theotherphil
54c9f03021
Mention in .add_observer() docs that first parameter must be a Trigger (#19315)
# Objective

Fix https://github.com/bevyengine/bevy/issues/13860

## Solution

Add note in docs that Trigger must be the first parameter of observer
systems
2025-05-26 20:06:08 +00:00
Benjamin Brienen
818a8fe154
Allow unfocused window creation (#19237)
# Objective

Allow creating a new window without it being focused, when `Window`'s
`focused` is `false.
## Solution

Use `winit`'s `WindowBuilder`'s `with_active` method

## Notes

- `winit`'s doc lists [redox's
`Orbital`](https://gitlab.redox-os.org/redox-os/orbital) as an
unsupported platform, but since Bevy doesn't officially support this
platform, I didn't put it in the documentation.

- I only tested on Linux, which is an unsupported platform. I can give
you a test code if you want to test on another platform.

- I initially put a line
[here](https://github.com/bevyengine/bevy/blob/v0.11.0/crates/bevy_winit/src/system.rs#L72)
to set the Bevy `Window`'s `focused` to `winit_window.has_focus()` after
window creation to avoid the case where `with_active` is not supported,
the window is spawned focused, no `WindowFocused` event is triggered,
and Bevy `Window` would be desynced from winit's window. But after
testing on Linux (which doesn't support `with_active`) it seems like at
that point `has_focus` returns `false` and the event is triggered, so I
removed it. Do you think I should add it back to be safe?

## Changelog

- A new unfocused `Window` can be created by setting `focused` to
`false`.

## Migration Guide

- If a `Window` is spawned with `focused` set to `false`, it will now
start not focused on supported platforms.

Adopted from #9208

---------

Co-authored-by: Sélène Amanita <selene.amanita@net-c.com>
Co-authored-by: Sélène Amanita <134181069+Selene-Amanita@users.noreply.github.com>
Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-05-26 20:05:33 +00:00
theotherphil
f8cb8f237d
Fix a few typos in bevy_ecs docs (#19280)
# Objective

Fix a few minor typos that I noticed when reading the docs.
2025-05-26 20:02:13 +00:00
theotherphil
348779850b
Add missing doc comments to system_information_diagnostics_plugin (#19267)
# Objective

More trivial doc comments towards being able to deny missing docs across
the board.
2025-05-26 19:59:49 +00:00
theotherphil
3690ad5b0b
Remove apostrophes in possessive its (#19244)
# Objective

Fix some grammatical errors: it's -> its

Not the most useful commit in the world, but I saw a couple of these and
decided to fix the lot.

## Solution
-

## Testing
-
2025-05-26 19:53:14 +00:00