Commit Graph

8441 Commits

Author SHA1 Message Date
Patrick Walton
0ede857103
Build batches across phases in parallel. (#17764)
Currently, invocations of `batch_and_prepare_binned_render_phase` and
`batch_and_prepare_sorted_render_phase` can't run in parallel because
they write to scene-global GPU buffers. After PR #17698,
`batch_and_prepare_binned_render_phase` started accounting for the
lion's share of the CPU time, causing us to be strongly CPU bound on
scenes like Caldera when occlusion culling was on (because of the
overhead of batching for the Z-prepass). Although I eventually plan to
optimize `batch_and_prepare_binned_render_phase`, we can obtain
significant wins now by parallelizing that system across phases.

This commit splits all GPU buffers that
`batch_and_prepare_binned_render_phase` and
`batch_and_prepare_sorted_render_phase` touches into separate buffers
for each phase so that the scheduler will run those phases in parallel.
At the end of batch preparation, we gather the render phases up into a
single resource with a new *collection* phase. Because we already run
mesh preprocessing separately for each phase in order to make occlusion
culling work, this is actually a cleaner separation. For example, mesh
output indices (the unique ID that identifies each mesh instance on GPU)
are now guaranteed to be sequential starting from 0, which will simplify
the forthcoming work to remove them in favor of the compute dispatch ID.

On Caldera, this brings the frame time down to approximately 9.1 ms with
occlusion culling on.

![Screenshot 2025-02-08
210720](https://github.com/user-attachments/assets/44bed500-e323-4786-b40c-828b75bc7d3f)
2025-02-13 00:02:20 +00:00
Chris Russell
62c1812e72
Shorten the 'world lifetime returned from QueryLens::query(). (#17694)
# Objective

Fix unsoundness introduced by #15858. `QueryLens::query()` would hand
out a `Query` with the full `'w` lifetime, and the new `_inner` methods
would let the results outlive the `Query`. This could be used to create
aliasing mutable references, like

```rust
fn bad<'w>(mut lens: QueryLens<'w, EntityMut>, entity: Entity) {
    let one: EntityMut<'w> = lens.query().get_inner(entity).unwrap();
    let two: EntityMut<'w> = lens.query().get_inner(entity).unwrap();
    assert!(one.entity() == two.entity());
}
```

Fixes #17693 

## Solution

Restrict the `'world` lifetime in the `Query` returned by
`QueryLens::query()` to `'_`, the lifetime of the borrow of the
`QueryLens`.

The model here is that `Query<'w, 's, D, F>` and `QueryLens<'w, D, F>`
have permission to access their components for the lifetime `'w`. So
going from `&'a mut QueryLens<'w>` to `Query<'w, 'a>` would borrow the
permission only for the `'a` lifetime, but incorrectly give it out for
the full `'w` lifetime.

To handle any cases where users were calling `get_inner()` or
`iter_inner()` on the `Query` and expecting the full `'w` lifetime, we
introduce a new `QueryLens::query_inner()` method. This is only valid
for `ReadOnlyQueryData`, so it may safely hand out a copy of the
permission for the full `'w` lifetime. Since `get_inner()` and
`iter_inner()` were only valid on `ReadOnlyQueryData` prior to #15858,
that should cover any uses that relied on the longer lifetime.

## Migration Guide

Users of `QueryLens::query()` who were calling `get_inner()` or
`iter_inner()` will need to replace the call with
`QueryLens::query_inner()`.
2025-02-12 22:41:02 +00:00
Patrick Walton
5ff7062c1c
Switch bins from parallel key/value arrays to IndexMaps. (#17819)
Conceptually, bins are ordered hash maps. We currently implement these
as a list of keys with an associated hash map. But we already have a
data type that implements ordered hash maps directly: `IndexMap`. This
patch switches Bevy to use `IndexMap`s for bins. Because we're memory
bound, this doesn't affect performance much, but it is cleaner.
2025-02-12 22:39:04 +00:00
Andreas Monitzer
267a0d003c
Add ComponentId-taking functions to Entity{Ref,Mut}Except to mirror FilteredEntity{Ref,Mut} (#17800)
# Objective

Related to #17784. The ticket is actually about just getting rid of
`Entity{Ref,Mut}Except` in favor of `FilteredEntity{Ref,Mut}`, but I got
told the unification of Entity types is a bigger endeavor that has been
going on for a while now (as the "Pointing Fingers" working group) and I
should just add the functions I actually need in the meantime.

## Solution

This PR adds all of the functions necessary to access components by
TypeId or ComponentId instead of static types.

## Testing

> Did you test these changes? If so, how?

Haven't tested it yet, but the changes are mostly copy/paste from other
implementations in the same file, since there is a lot of duplicated
functionality there.

## Not a Migration Guide

There shouldn't be any breaking changes, it's just a few new functions
on existing types.

I had to shuffle around the lifetimes in `From<&EntityMutExcept<'a, B>>
for EntityRefExcept<'a, B>` (originally it was `From<&'a
EntityMutExcept<'_, B>> for EntityRefExcept<'_, B>`) to make the borrow
checker happy, but I don't think that this should have an impact on user
code (correct me if I'm wrong).
2025-02-12 18:34:35 +00:00
JMS55
2fd4cc4937
Meshlet texture atomics (#17765)
* Use texture atomics rather than buffer atomics for the visbuffer
(haven't tested perf on a raster-heavy scene yet)
* Unfortunately to clear the visbuffer we now need a compute pass to
clear it. Using wgpu's clear_texture function internally uses a buffer
-> image copy that's insanely expensive. Ideally it should be using
vkCmdClearColorImage, which I've opened an issue for
https://github.com/gfx-rs/wgpu/issues/7090. For now we'll have to stick
with a custom compute pass and all the extra code that brings.
* Faster resolve depth pass by discarding 0 depth pixels instead of
redundantly writing zero (2x faster for big depth textures like shadow
views)
2025-02-12 18:15:43 +00:00
Rob Parrett
2f9613f22c
Minor tidying in font_atlas_debug (#17825)
# Objective

Tidy up a few little things I noticed while working with this example

## Solution

- Fix manual resetting of a repeating timer
- Use atlas image size instead of hardcoded value. Atlases are always
512x512 right now, but hopefully not in the future.
- Pluralize a variable name for a variable holding a `Vec`
2025-02-12 18:14:34 +00:00
Rob Parrett
c34eaf13be
Add separate option to control font size behavior in many_text2d (#17823)
# Objective

I'm working on some PRs involving our font atlases and it would be nice
to be able to test these scenarios separately to better understand the
performance tradeoffs in different situations.

## Solution

Add a `many-font-sizes` option.

The old behavior is still available by running with `--many-glyphs
--many-font-sizes`.

## Testing

`cargo run --example many_text2d --release`
`cargo run --example many_text2d --release -- --many-glyphs`
`cargo run --example many_text2d --release -- --many-font-sizes`
`cargo run --example many_text2d --release -- --many-glyphs
--many-font-sizes`
2025-02-12 17:25:20 +00:00
JMS55
15b795d7d6
Use unchecked shaders for better performance (#17767)
# Objective
- Wgpu has some expensive code it injects into shaders to avoid the
possibility of things like infinite loops. Generally our shaders are
written by users who won't do this, so it just makes our shaders perform
worse.

## Solution

- Turn off the checks.
- We could try to conditionally keep them, but that complicates the code
and 99.9% of users won't want this.

## Migration Guide

- Bevy no longer turns on wgpu's runtime safety checks
https://docs.rs/wgpu/latest/wgpu/struct.ShaderRuntimeChecks.html. If you
were using Bevy with untrusted shaders, please file an issue.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-02-12 06:16:52 +00:00
Vic
153ce468ef
implement iterators that yield UniqueEntitySlice (#17796)
# Objective

Continuation of #17589 and #16547.

Slices have several methods that return iterators which themselves yield
slices, which we have not yet implemented.
An example use is `par_iter_many` style logic.

## Solution

Their implementation is rather straightforward, we simply delegate all
impls to `[T]`.
The resulting iterator types need their own wrappers in the form of
`UniqueEntitySliceIter` and `UniqueEntitySliceIterMut`.

We also add three free functions that cast slices of entity slices to
slices of `UniqueEntitySlice`.
These three should be sufficient, though infinite nesting is achievable
with a trait (like `TrustedEntityBorrow` works over infinite reference
nesting), should the need ever arise.
2025-02-12 03:59:56 +00:00
François Mockers
cff17364b1
Deterministic fallible_systems example (#17813)
# Objective

- `fallible_systems` example is not deterministic

## Solution

- Make it deterministic
- Also fix required feature declaration
2025-02-11 23:54:20 +00:00
Rob Parrett
7398d33b79
Panic on failure in scene example (#17812)
# Objective

Fixes #17810

Y'all picked the option I already implemented, yay.

## Solution

Add a system that panics if the load state of an asset is `Failed`.

## Testing

`cargo run --example scene`

- Tested with valid scene file
- Introduced a syntax error in the scene file
- Deleted the scene file
2025-02-11 23:11:27 +00:00
Mads Marquart
94deca81bf
Use target_abi = "sim" instead of ios_simulator feature (#17702)
## Objective

Get rid of a redundant Cargo feature flag.

## Solution

Use the built-in `target_abi = "sim"` instead of a custom Cargo feature
flag, which is set for the iOS (and visionOS and tvOS) simulator. This
has been stable since Rust 1.78.

In the future, some of this may become redundant if Wgpu implements
proper supper for the iOS Simulator:
https://github.com/gfx-rs/wgpu/issues/7057

CC @mockersf who implemented [the original
fix](https://github.com/bevyengine/bevy/pull/10178).

## Testing

- Open mobile example in Xcode.
- Launch the simulator.
- See that no errors are emitted.
- Remove the code cfg-guarded behind `target_abi = "sim"`.
- See that an error now happens.

(I haven't actually performed these steps on the latest `main`, because
I'm hitting an unrelated error (EDIT: It was
https://github.com/bevyengine/bevy/pull/17637). But tested it on
0.15.0).

---

## Migration Guide

> If you're using a project that builds upon the mobile example, remove
the `ios_simulator` feature from your `Cargo.toml` (Bevy now handles
this internally).
2025-02-11 23:01:26 +00:00
Patrick Walton
85b366a8a2
Cache MeshInputUniform indices in each RenderBin. (#17772)
Currently, we look up each `MeshInputUniform` index in a hash table that
maps the main entity ID to the index every frame. This is inefficient,
cache unfriendly, and unnecessary, as the `MeshInputUniform` index for
an entity remains the same from frame to frame (even if the input
uniform changes). This commit changes the `IndexSet` in the `RenderBin`
to an `IndexMap` that maps the `MainEntity` to `MeshInputUniformIndex`
(a new type that this patch adds for more type safety).

On Caldera with parallel `batch_and_prepare_binned_render_phase`, this
patch improves that function from 3.18 ms to 2.42 ms, a 31% speedup.
2025-02-11 22:38:52 +00:00
Patrick Walton
ce433955e6
Don't relocate the meshes when mesh slabs grow. (#17793)
Currently, when a mesh slab overflows, we recreate the allocator and
reinsert all the meshes that were in it in an arbitrary order. This can
result in the meshes moving around. Before `MeshInputUniform`s were
retained, this was slow but harmless, because the `MeshInputUniform`
that contained the positions of the vertex and index data in the slab
would be recreated every frame. However, with mesh retention, there's no
guarantee that the `MeshInputUniform`, which could be cached from the
previous frame, will reflect the new position of the mesh data within
the buffer if that buffer happened to grow. This manifested itself as
seeming mesh data corruption when adding many meshes dynamically to the
scene.

There are three possible ways that I could have fixed this that I can
see:

1. Invalidate and rebuild all the `MeshInputUniform`s belonging to all
meshes in a slab when that mesh grows.

2. Introduce a second layer of indirection so that the
`MeshInputUniform` points to a *mesh allocation table* that contains the
current locations of the data of each mesh.

3. Avoid moving meshes when reallocating the buffer.

To be efficient, option (1) would require scanning meshes to see if
their positions changed, a la
`mark_meshes_as_changed_if_their_materials_changed`. Option (2) would
add more runtime indirection and would require additional bookkeeping on
the part of the allocator.

Therefore, this PR chooses option (3), which was remarkably simple to
implement. The key is that the offset allocator happens to allocate
addresses from low addresses to high addresses. So all we have to do is
to *conceptually* allocate the full 512 MiB mesh slab as far as the
offset allocator is concerned, and grow the underlying backing store
from 1 MiB to 512 MiB as needed. In other words, the allocator now
allocates *virtual* GPU memory, and the actual backing slab resizes to
fit the virtual memory. This ensures that the location of mesh data
remains constant for the lifetime of the mesh asset, and we can remove
the code that reinserts meshes one by one when the slab grows in favor
of a single buffer copy.

Closes #17766.
2025-02-11 22:38:26 +00:00
François Mockers
7a62a4f604
gate get_tag behind ndef MESHLET_MESH_MATERIAL_PASS (#17809)
# Objective

- Fixes #17797 

## Solution

- `mesh` in `bevy_pbr::mesh_bindings` is behind a `ifndef
MESHLET_MESH_MATERIAL_PASS`. also gate `get_tag` which uses this `mesh`

## Testing

- Run the meshlet example
2025-02-11 22:36:17 +00:00
Lucas Franca
fc98664d3d
Add tonemapping switch to bloom 2d example (#17789)
# Objective

Allow switching through available Tonemapping algorithms on `bloom_2d`
example to compare between them

## Solution

Add a resource to `bloom_2d` that holds current tonemapping algorithm, a
method to get the next one, and a check of key press to make the switch

## Testing

Ran `bloom_2d` example with modified code

## Showcase


https://github.com/user-attachments/assets/920b2d6a-b237-4b19-be9d-9b651b4dc913
Note: Sprite flashing is already described in #17763
2025-02-11 22:21:04 +00:00
Rob Parrett
fe7a29e12b
Fix error in scene example (#17799)
# Objective

After #16894, this example started logging errors:

```
ERROR bevy_asset::server: Failed to load asset 'scenes/load_scene_example.scn.ron' with asset loader 'bevy_scene::scene_loader::SceneLoader': Could not parse RON: 10:33: Expected string
```

Fixes #17798, this is the only actionable/unreported issue in there as
far as I can tell.

## Solution

Update the serialized scene with the expected format for `Name`

## Testing

`cargo run --example scene`

## Discussion

This example breaks very often and we don't always catch it. It might be
nice to have this scene either

1. produce visual output so that it can be checked
2. panic if the scene fails to load (check for LoadState::Failed)

Either of those would make the failures visible in [the example
report](https://thebevyflock.github.io/bevy-example-runner/). Not sure
which method would best suit the example.
2025-02-11 22:20:23 +00:00
Rob Parrett
0cb3eaef67
Fix validation errors in Fox.glb (#17801)
# Objective

Fix gltf validation errors in `Fox.glb`.

Inspired by #8099, but that issue doesn't appear to describe a real bug
to fix, as far as I can tell.

## Solution

Use the latest version of the Fox from
[glTF-Sample-Assets](https://github.com/KhronosGroup/glTF-Sample-Assets/blob/main/Models/Fox/glTF-Binary/Fox.glb).

## Testing

Dropped both versions in https://github.khronos.org/glTF-Validator/

`cargo run --example animated_mesh` seems to still look fine.

Before:

```
The asset contains errors.
"numErrors": 126,
"numWarnings": 4184,
```

After:

```
The asset is valid.
"numErrors": 0,
"numWarnings": 0,
```

## Discussion

The 3d testbed was panicking with
```
thread 'main' panicked at examples/testbed/3d.rs:288:60:
called `Result::unwrap()` on an `Err` value: QueryDoesNotMatch(35v1 with components Transform, GlobalTransform, Visibility, InheritedVisibility, ViewVisibility, ChildOf, Children, Name)
```
Which is bizarre. I think this might be related to #17720, or maybe the
structure of the gltf changed.

I fixed it by using updating the testbed to use a more robust method of
finding the correct entity as is done in `animated_mesh`.
2025-02-11 22:19:24 +00:00
RobWalt
aa8793f6b4
Add ways to configure EasingFunction::Steps via new StepConfig (#17752)
# Objective

- In #17743, attention was raised to the fact that we supported an
unusual kind of step easing function. The author of the fix kindly
provided some links to standards used in CSS. It would be desirable to
support generally agreed upon standards so this PR here tries to
implement an extra configuration option of the step easing function
- Resolve #17744

## Solution

- Introduce `StepConfig`
- `StepConfig` can configure both the number of steps and the jumping
behavior of the function
- `StepConfig` replaces the raw `usize` parameter of the
`EasingFunction::Steps(usize)` construct.
- `StepConfig`s default jumping behavior is `end`, so in that way it
follows #17743

## Testing

- I added a new test per `JumpAt` jumping behavior. These tests
replicate the visuals that can be found at
https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function/steps#description

## Migration Guide

- `EasingFunction::Steps` now uses a `StepConfig` instead of a raw
`usize`. You can replicate the previous behavior by replaceing
`EasingFunction::Steps(10)` with
`EasingFunction::Steps(StepConfig::new(10))`.

---------

Co-authored-by: François Mockers <francois.mockers@vleue.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-02-11 22:19:01 +00:00
ickshonpe
98dcee2853
UI text extraction refactor (#17805)
## Objective

There's no need for the `span_index` and `color` variables in
`extract_text_shadows` and `extract_text_sections` and we can remove one
of the span index comparisons since text colors are only set per
section.

## Testing

<img width="454" alt="trace"
src="https://github.com/user-attachments/assets/3109d1df-0817-46c2-9889-0459ac93a42c"
/>
2025-02-11 22:18:47 +00:00
Jean Mertz
7d8504f30e
feat(ecs): implement fallible observer systems (#17731)
This commit builds on top of the work done in #16589 and #17051, by
adding support for fallible observer systems.

As with the previous work, the actual results of the observer system are
suppressed for now, but the intention is to provide a way to handle
errors in a global way.

Until then, you can use a `PipeSystem` to manually handle results.

---------

Signed-off-by: Jean Mertz <git@jeanmertz.com>
2025-02-11 22:15:43 +00:00
SpecificProtagonist
5b0d898866
Trait tags on docs.rs (#17758)
# Objective

Bevy's ECS provides several core traits such as `Component`,
`SystemParam`, etc that determine where a type can be used. When reading
the docs, this currently requires scrolling down to and scanning the
"Trait Implementations" section. Make these core traits more visible.

## Solution

Add a color-coded labels below the type heading denoting the following
types:
- `Component`
  - immutable components are labeled as such
- `Resource`
- `Asset`
- `Event`
- `Plugin` & `PluginGroup`
- `ScheduleLabel` & `SystemSet`
- `SystemParam`

As docs.rs does not provide an option for post-processing the html,
these are added via JS with traits implementations being detected by
scanning the DOM. Rustdoc's html output is unstable, which could
potentially lead to this detection (or the adding of the labels) to
break, however it only needs to work when a new release is deployed and
falls back to the status quo of not displaying these labels.

Idea by JMS55, implementation by Jondolf (see
https://github.com/Jondolf/bevy_docs_extension_demo/).

## Testing

Run this in Bevy's root folder:
```bash
 RUSTDOCFLAGS="--html-after-content docs-rs/trait-tags.html --cfg docsrs_dep" RUSTFLAGS="--cfg docsrs_dep" cargo doc --no-deps -p <some_bevy_package>
```

---

## Showcase
Check it out on
[docs.rs](https://docs.rs/bevy_docs_extension_demo/0.1.1/bevy_docs_extension_demo/struct.TestAllTraits.html)

![trait
tags](https://github.com/user-attachments/assets/a30d8324-41fd-432a-8e49-6d475f143725)

## Release Notes

On docs.rs, Bevy now displays labels indicating which core traits a type
implements:
![trait tags
small](https://github.com/user-attachments/assets/c69b565f-e4bc-4277-9f6b-40830031077d)

If you want to add these to your own crate, check out [these
instructions](https://github.com/bevyengine/bevy/blob/main/docs-rs#3rd-party-crates).

---------

Co-authored-by: Joona Aalto <jondolf.dev@gmail.com>
Co-authored-by: Carter Weinberg <weinbergcarter@gmail.com>
2025-02-11 22:13:38 +00:00
Periwink
d6725d3b1b
Expose method to update the internal ticks of Ref and Mut (#17716)
## What problem does this solve or what need does it fill?

There are some situations
(https://github.com/bevyengine/bevy/issues/13735) where the ticks that
are present inside `Ref` are incorrect, for example if `Ref` is created
outside of a `SystemParam`.
I still want to use `Ref` because it has convenient `is_added` and
`is_changed` methods.

My current solution is to build my own `Ref` by copy-pasting most the
bevy code to do that via something like
```rust
/// This method is necessary because there is no easy way to 
pub(crate) fn get_ref<C: Component>(
    world: &World,
    entity: Entity,
    last_run: Tick,
    this_run: Tick,
) -> Ref<C> {
    unsafe {
        let component_id = world
            .components()
            .get_id(TypeId::of::<C>())
            .unwrap_unchecked();
        let world = world.as_unsafe_world_cell_readonly();
        let entity_cell = world.get_entity(entity).unwrap_unchecked();
        get_component_and_ticks(
            world,
            component_id,
            C::STORAGE_TYPE,
            entity,
            entity_cell.location(),
        )
        .map(|(value, cells, _caller)| {
            Ref::new(
                value.deref::<C>(),
                cells.added.deref(),
                cells.changed.deref(),
                last_run,
                this_run,
                #[cfg(feature = "track_location")]
                _caller.deref(),
            )
        })
        .unwrap_unchecked()
    }
}

// Utility function to return
#[inline]
unsafe fn get_component_and_ticks(
    world: UnsafeWorldCell<'_>,
    component_id: ComponentId,
    storage_type: StorageType,
    entity: Entity,
    location: EntityLocation,
) -> Option<(Ptr<'_>, TickCells<'_>, MaybeUnsafeCellLocation<'_>)> {
    match storage_type {
        StorageType::Table => {
            let table = unsafe { world.storages().tables.get(location.table_id) }?;

            // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules
            Some((
                table.get_component(component_id, location.table_row)?,
                TickCells {
                    added: table
                        .get_added_tick(component_id, location.table_row)
                        .unwrap_unchecked(),
                    changed: table
                        .get_changed_tick(component_id, location.table_row)
                        .unwrap_unchecked(),
                },
                #[cfg(feature = "track_location")]
                table
                    .get_changed_by(component_id, location.table_row)
                    .unwrap_unchecked(),
                #[cfg(not(feature = "track_location"))]
                (),
            ))
        }
        StorageType::SparseSet => {
            let storage = unsafe { world.storages() }.sparse_sets.get(component_id)?;
            storage.get_with_ticks(entity)
        }
    }
}
```

It would be very convenient if instead bevy exposed a way to create a
`Ref` object with custom `last_run` and `this_run` ticks.
This PR does this by exposing a function to overwrite the `last_run` and
`this_run` ticks.
(Same with `Mut`)

I am ok with marking the method unsafe or risky if it's deemed to risky
for end-users.
2025-02-11 19:00:13 +00:00
sam edelsten
5eff6e80e1
Add relative position reporting to UI picking (#17681)
# Objective

Add position reporting to `HitData` sent from the UI picking backend.

## Solution

Add the computed normalized relative cursor position to `hit_data`
alongside the `Entity`.

The position reported in `HitData` is normalized relative to the node,
with `(0.,0.,0.)` at the top left and `(1., 1., 0.)` in the bottom
right. Coordinates are relative to the entire node, not just the visible
region.

`HitData` needs a `Vec3` so I just extended with 0.0. I considered
inserting the `depth` here but thought it would be redundant.

I also considered putting the screen space position in the `normal`
field of `HitData`, but that would require renaming of the field or a
separate data structure.

## Testing

Tested with mouse on X11 with entities that have `Node` components.

---

## Showcase

```rs
// Get click position relative to node
fn hit_position(trigger: Trigger<Pointer<Click>>) {
    let hit_pos = trigger.event.hit.position.expect("no position");
    info!("{}", hit_pos);
}
```

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-02-11 18:38:13 +00:00
Jean Mertz
fd67ca7eb0
feat(ecs): configurable error handling for fallible systems (#17753)
You can now configure error handlers for fallible systems. These can be
configured on several levels:

- Globally via `App::set_systems_error_handler`
- Per-schedule via `Schedule::set_error_handler`
- Per-system via a piped system (this is existing functionality)

The default handler of panicking on error keeps the same behavior as
before this commit.

The "fallible_systems" example demonstrates the new functionality.

This builds on top of #17731, #16589, #17051.

---------

Signed-off-by: Jean Mertz <git@jeanmertz.com>
2025-02-11 18:36:08 +00:00
ickshonpe
c896ad6146
Split the labels in many_buttons (#17802)
# Objective

Add some multi-span text to the `many_buttons` benchmark by splitting up
each button label text into two different coloured text spans.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-02-11 18:29:06 +00:00
raldone01
fb0e5c484f
Fix failing proc macros when depending on bevy through dev and normal dependencies. (#17795)
This is a follow up fix for #17330 and fixes #17780.
There was a logic error in the ambiguity detection of
`cargo-manifest-proc-macros`.
`cargo-manifest-proc-macros` now has a test for this case to prevent the
issue in the future.

I also opted to hard fail if the `cargo-manifest-proc-macros` crate
fails. That way the error is more obvious and easier to fix and
diagnose.

## Testing

- The reproducer:
https://github.com/bevyengine/bevy_editor_prototypes/pull/178 works for
me using these fixes.
2025-02-11 18:28:15 +00:00
Alice Cecile
fcc77fe3d6
Allow users to register their own disabling components / default query filters (#17768)
# Objective

Currently, default query filters, as added in #13120 / #17514 are
hardcoded to only use a single query filter.

This is limiting, as multiple distinct disabling components can serve
important distinct roles. I ran into this limitation when experimenting
with a workflow for prefabs, which don't represent the same state as "an
entity which is temporarily nonfunctional".

## Solution

1. Change `DefaultQueryFilters` to store a SmallVec of ComponentId,
rather than an Option.
2. Expose methods on `DefaultQueryFilters`, `World` and `App` to
actually configure this.
3. While we're here, improve the docs, write some tests, make use of
FromWorld and make some method names more descriptive.

## Follow-up

I'm not convinced that supporting sparse set disabling components is
useful, given the hit to iteration performance and runtime checks
incurred. That's disjoint from this PR though, so I'm not doing it here.
The existing warnings are fine for now.

## Testing

I've added both a doc test and an mid-level unit test to verify that
this works!
2025-02-11 18:25:32 +00:00
Lucas Franca
5e9da923f9
Add links to the types on the documentation of GltfAssetLabel (#17791)
# Objective

Allow quick jump to definition of types of GlTFs labeled assets.

## Solution

Add links to the types refered on the docs of `GltfAssetLabel`

## Testing

Ran `cargo run -p ci`
2025-02-11 01:30:41 +00:00
person93
575f66504b
Silence deprecation warning in Bundle derive macro (#17369) (#17790)
# Objective

- Fixes #17369

## Solution

- Add `#[allow(deprecated)]` to the generated code.
2025-02-11 00:56:09 +00:00
Emerson Coskey
83370e0a25
Use dual-source blending for rendering the sky (#17672)
# Objective

Since previously we only had the alpha channel available, we stored the
mean of the transmittance in the aerial view lut, resulting in a grayer
fog than should be expected.

## Solution

- Calculate transmittance to scene in `render_sky` with two samples from
the transmittance lut
- use dual-source blending to effectively have per-component alpha
blending
2025-02-10 23:53:53 +00:00
Patrick Walton
69db29efb9
Sweep bins after queuing so as to only sweep them once. (#17787)
Currently, we *sweep*, or remove entities from bins when those entities
became invisible or changed phases, during `queue_material_meshes` and
similar phases. This, however, is wrong, because `queue_material_meshes`
executes once per material type, not once per phase. This could result
in sweeping bins multiple times per phase, which can corrupt the bins.
This commit fixes the issue by moving sweeping to a separate system that
runs after queuing.

This manifested itself as entities appearing and disappearing seemingly
at random.

Closes #17759.

---------

Co-authored-by: Robert Swain <robert.swain@gmail.com>
2025-02-10 23:15:35 +00:00
charlotte
a861452d68
Add user supplied mesh tag (#17648)
# Objective

Because of mesh preprocessing, users cannot rely on
`@builtin(instance_index)` in order to reference external data, as the
instance index is not stable, either from frame to frame or relative to
the total spawn order of mesh instances.

## Solution

Add a user supplied mesh index that can be used for referencing external
data when drawing instanced meshes.

Closes #13373

## Testing

Benchmarked `many_cubes` showing no difference in total frame time.

## Showcase



https://github.com/user-attachments/assets/80620147-aafc-4d9d-a8ee-e2149f7c8f3b

---------

Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2025-02-10 22:38:13 +00:00
Greeble
71b22397da
Expand EasingCurve documentation (#17778)
# Objective

- Expand the documentation for `EasingCurve`.
- I suspect this might have avoided the confusion in
https://github.com/bevyengine/bevy/pull/17711.
- Also add a shortcut for simple cases.

## Solution

- Added various examples and extra context.
- Implemented `Curve<T>` for `EaseFunction`.
- This means `EasingCurve::new(0.0, 1.0, EaseFunction::X)` can be
shortened to `EaseFunction::X`.
    - In some cases this will be a minor performance improvement.
    - Added test to confirm they're the same.
- ~~Added some benchmarks for bonus points.~~


## Side Notes

- I would have liked to rename `EaseFunction` to `EaseFn` for brevity,
but that would be a breaking change and maybe controversial.
    - Also suspect `EasingCurve` should be `EaseCurve`, but say la vee.
- Benchmarks show that calling `EaseFunction::Smoothstep` is still
slower than calling `smoothstep` directly.
- I think this is because the compiler refuses to inline
`EaseFunction::eval`.
- I don't see any good solution - might need a whole different
interface.

## Testing

```sh
cargo test --package bevy_math

cargo doc --package bevy_math
./target/doc/bevy_math/curve/easing/struct.EasingCurve.html

cargo bench --package benches --bench math -- easing
```
2025-02-10 22:37:27 +00:00
François Mockers
7d141829be
run example in CI on windows using static dxc (#17783)
# Objective

- Run more things on windows

## Solution

- With the update of wgpu and the statically linked dxc, examples now
run on windows in CI
2025-02-10 22:35:41 +00:00
ickshonpe
359cd432c0
UI clipping update function comments fix (#17785)
# Objective
Fix for the comments for the clipping rects update function which
references `Overflow` variants that no longer exist.
2025-02-10 22:35:12 +00:00
jf908
f27e00b197
Derive Reflect on Skybox (#17781)
# Objective

- Derive Reflect on Skybox component

## Solution

- Derive Reflect on Skybox component
2025-02-10 22:24:23 +00:00
Lege19
3978ba9783
Allowed creating uninitialized images (for use as storage textures) (#17760)
# Objective
https://github.com/bevyengine/bevy/issues/17746
## Solution
- Change `Image.data` from being a `Vec<u8>` to a `Option<Vec<u8>>`
- Added functions to help with creating images
## Testing

- Did you test these changes? If so, how?
All current tests pass
Tested a variety of existing examples to make sure they don't crash
(they don't)
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
Linux x86 64-bit NixOS 
---
## Migration Guide
Code that directly access `Image` data will now need to use unwrap or
handle the case where no data is provided.
Behaviour of new_fill slightly changed, but not in a way that is likely
to affect anything. It no longer panics and will fill the whole texture
instead of leaving black pixels if the data provided is not a nice
factor of the size of the image.

---------

Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2025-02-10 22:22:07 +00:00
fschlee
db0356517e
CosmicBuffer is a public type but not not used or accessible in any public API (#17748)
# Objective

Currently
[CosmicBuffer](https://docs.rs/bevy/latest/bevy/text/struct.CosmicBuffer.html)
is a public type with a public field that is not used or accessible in
any public API. Since it is prominently shown in the docs it is the
obvious place to start when trying to access `cosmic_string` features
such as for mapping between screen coordinates and positions in the
displayed text.
The only place `CosmicBuffer` is currently used is as a field of
`ComputedTextBlock`, where a comment explains why the field is private:

    /// Buffer for managing text layout and creating [`TextLayoutInfo`].
    ///
/// This is private because buffer contents are always refreshed from
ECS state when writing glyphs to
/// `TextLayoutInfo`. If you want to control the buffer contents
manually or use the `cosmic-text`
/// editor, then you need to not use `TextLayout` and instead manually
implement the conversion to
    /// `TextLayoutInfo`.
    #[reflect(ignore)]
    pub(crate) buffer: CosmicBuffer,
    
Unfortunately this comment does not appear in the docs, so a user
looking for a way to access `CosmicBuffer` will not find it unless they
check the source code.
Also there does not seem to be any alternative way to map between screen
coordinates and positions in the displayed text, which would be highly
useful for things like text edit widgets or tool tips. The reasons given
for making the field private only apply for mutable access, so
non-mutable access would presumably be fine.
## Solution

I added a getter to `ComputedTextBlock`, and added the explanation for
why there is no mutable access in the comment:

/// Accesses the underling buffer which can be used for `cosmic-text`
APIs such as accessing layout information
    /// or calculating a cursor position.
    ///
/// Mutable access not offered because changes would be overwritten
during the automated layout calculation.
/// If you want to control the buffer contents manually or use the
`cosmic-text`
/// editor, then you need to not use `TextLayout` and instead manually
implement the conversion to
    /// `TextLayoutInfo`.
	pub fn get_buffer(&self) -> &CosmicBuffer {
		&self.buffer
	}

## Testing

I tested that the getter could be used to map from screen coordinates to
string positions by creating a rudimentary text edit widget and trying
it out.

## Alternatives
An alternative to making `CosmicBuffer` accessible would be to make the
type private so that no one wastes time looking for a way of accessing
it, and adding additional methods to `ComputedTextBlock` that make use
of the buffer as implementation detail and offer access to currently
inaccessible functionality.

---------

Co-authored-by: Rob Parrett <robparrett@gmail.com>
2025-02-10 22:19:12 +00:00
François Mockers
4fe57767fc
make bevy math publishable (#17727)
# Objective

- bevy_math fails to publish because of the self dev-dependency
- it's used to enable the `approx` feature in tests

## Solution

- Don't specify a version in the dev-dependency. dependencies without a
version are ignored by cargo when publishing
- Gate all the tests that depend on the `approx` feature so that it
doesn't fail to compile when not enabled
- Also gate an import that wasn't used without `bevy_reflect`

## Testing

- with at least cargo 1.84: `cargo package -p bevy_math`
- `cd target/package/bevy_math_* && cargo test`
2025-02-10 22:15:53 +00:00
Chris Russell
c34a2c2fba
Query::get_many should not check for duplicates (#17724)
# Objective

Restore the behavior of `Query::get_many` prior to #15858.  

When passed duplicate `Entity`s, `get_many` is supposed to return
results for all of them, since read-only queries don't alias. However,
#15858 merged the implementation with `get_many_mut` and caused it to
return `QueryEntityError::AliasedMutability`.

## Solution

Introduce a new `Query::get_many_readonly` method that consumes the
`Query` like `get_many_inner`, but that is constrained to `D:
ReadOnlyQueryData` so that it can skip the aliasing check. Implement
`Query::get_many` in terms of that new method. Add a test, and a comment
explaining why it doesn't match the pattern of the other `&self`
methods.
2025-02-10 22:07:15 +00:00
urben1680
7f9588d4c6
Fix documentation of Entities::get (#17721)
This method returns `None` if `meta.location.archetype_id` is
`ArchetypeId::INVALID`.
`EntityLocation::INVALID.archetype_id` is `ArchetypeId::INVALID`.
Therefore this method cannot return `Some(EntityLocation::INVALID)`.
Linking to it in the docs is futile anyway as that constant is not
public.
2025-02-10 22:05:05 +00:00
colepoirier
84359514bd
Add scroll functionality to bevy_picking (#17704)
# Objective

`bevy_picking` currently does not support scroll events.

## Solution

This pr adds a new event type for scroll, and updates the default input
system for mouse pointers to read and emit this event.

## Testing

- Did you test these changes? If so, how?
- Are there any parts that need more testing?
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?

I haven't tested these changes, if the reviewers can advise me how to do
so I'd appreciate it!
2025-02-10 22:03:38 +00:00
mgi388
2660ddc4c5
Support decibels in bevy_audio::Volume (#17605)
# Objective

- Allow users to configure volume using decibels by changing the
`Volume` type from newtyping an `f32` to an enum with `Linear` and
`Decibels` variants.
- Fixes #9507.
- Alternative reworked version of closed #9582.

## Solution

Compared to https://github.com/bevyengine/bevy/pull/9582, this PR has
the following main differences:

1. It uses the term "linear scale" instead of "amplitude" per
https://github.com/bevyengine/bevy/pull/9582/files#r1513529491.
2. Supports `ops` for doing `Volume` arithmetic. Can add two volumes,
e.g. to increase/decrease the current volume. Can multiply two volumes,
e.g. to get the “effective” volume of an audio source considering global
volume.

[requested and blessed on Discord]:
https://discord.com/channels/691052431525675048/749430447326625812/1318272597003341867

## Testing

- Ran `cargo run --example soundtrack`.
- Ran `cargo run --example audio_control`.
- Ran `cargo run --example spatial_audio_2d`.
- Ran `cargo run --example spatial_audio_3d`.
- Ran `cargo run --example pitch`.
- Ran `cargo run --example decodable`.
- Ran `cargo run --example audio`.

---

## Migration Guide

Audio volume can now be configured using decibel values, as well as
using linear scale values. To enable this, some types and functions in
`bevy_audio` have changed.

- `Volume` is now an enum with `Linear` and `Decibels` variants.

Before:

```rust
let v = Volume(1.0);
```

After:

```rust
let volume = Volume::Linear(1.0);
let volume = Volume::Decibels(0.0); // or now you can deal with decibels if you prefer
```

- `Volume::ZERO` has been renamed to the more semantically correct
`Volume::SILENT` because `Volume` now supports decibels and "zero
volume" in decibels actually means "normal volume".
- The `AudioSinkPlayback` trait's volume-related methods now deal with
`Volume` types rather than `f32`s. `AudioSinkPlayback::volume()` now
returns a `Volume` rather than an `f32`. `AudioSinkPlayback::set_volume`
now receives a `Volume` rather than an `f32`. This affects the
`AudioSink` and `SpatialAudioSink` implementations of the trait. The
previous `f32` values are equivalent to the volume converted to linear
scale so the `Volume:: Linear` variant should be used to migrate between
`f32`s and `Volume`.
- The `GlobalVolume::new` function now receives a `Volume` instead of an
`f32`.

---------

Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2025-02-10 21:26:43 +00:00
Chris Russell
eee7fd5b3e
Encapsulate cfg(feature = "track_location") in a type. (#17602)
# Objective

Eliminate the need to write `cfg(feature = "track_location")` every time
one uses an API that may use location tracking. It's verbose, and a
little intimidating. And it requires code outside of `bevy_ecs` that
wants to use location tracking needs to either unconditionally enable
the feature, or include conditional compilation of its own. It would be
good for users to be able to log locations when they are available
without needing to add feature flags to their own crates.

Reduce the number of cases where code compiles with the `track_location`
feature enabled, but not with it disabled, or vice versa. It can be hard
to remember to test it both ways!

Remove the need to store a `None` in `HookContext` when the
`track_location` feature is disabled.

## Solution

Create an `MaybeLocation<T>` type that contains a `T` if the
`track_location` feature is enabled, and is a ZST if it is not. The
overall API is similar to `Option`, but whether the value is `Some` or
`None` is set at compile time and is the same for all values.

Default `T` to `&'static Location<'static>`, since that is the most
common case.

Remove all `cfg(feature = "track_location")` blocks outside of the
implementation of that type, and instead call methods on it.

When `track_location` is disabled, `MaybeLocation` is a ZST and all
methods are `#[inline]` and empty, so they should be entirely removed by
the compiler. But the code will still be visible to the compiler and
checked, so if it compiles with the feature disabled then it should also
compile with it enabled, and vice versa.

## Open Questions

Where should these types live? I put them in `change_detection` because
that's where the existing `MaybeLocation` types were, but we now use
these outside of change detection.

While I believe that the compiler should be able to remove all of these
calls, I have not actually tested anything. If we want to take this
approach, what testing is required to ensure it doesn't impact
performance?

## Migration Guide

Methods like `Ref::changed_by()` that return a `&'static
Location<'static>` will now be available even when the `track_location`
feature is disabled, but they will return a new `MaybeLocation` type.
`MaybeLocation` wraps a `&'static Location<'static>` when the feature is
enabled, and is a ZST when the feature is disabled.

Existing code that needs a `&Location` can call `into_option().unwrap()`
to recover it. Many trait impls are forwarded, so if you only need
`Display` then no changes will be necessary.

If that code was conditionally compiled, you may instead want to use the
methods on `MaybeLocation` to remove the need for conditional
compilation.

Code that constructs a `Ref`, `Mut`, `Res`, or `ResMut` will now need to
provide location information unconditionally. If you are creating them
from existing Bevy types, you can obtain a `MaybeLocation` from methods
like `Table::get_changed_by_slice_for()` or
`ComponentSparseSet::get_with_ticks`. Otherwise, you will need to store
a `MaybeLocation` next to your data and use methods like `as_ref()` or
`as_mut()` to obtain wrapped references.
2025-02-10 21:21:20 +00:00
IceSentry
4ecbe001d5
Add a custom render phase example (#16916)
# Objective

- It's currently very hard for beginners and advanced users to get a
full understanding of a complete render phase.

## Solution

- Implement a full custom render phase
- The render phase in the example is intended to show a custom stencil
phase that renders the stencil in red directly on the screen

---

## Showcase

<img width="1277" alt="image"
src="https://github.com/user-attachments/assets/e9dc0105-4fb6-463f-ad53-0529b575fd28"
/>

## Notes

More docs to explain what is going on is still needed but the example
works and can already help some people.

We might want to consider using a batched phase and cold specialization
in the future, but the example is already complex enough as it is.

---------

Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
2025-02-10 21:17:37 +00:00
Mike
8e84b461a0
many_components stress test improvements (#16913)
# Objective

- I was getting familiar with the many_components example to test some
recent pr's for executor changes and saw some things to improve.

## Solution

- Use `insert_by_ids` instead of `insert_by_id`. This reduces the number
of archetype moves and improves startup times substantially.
- Add a tracing span to `base_system`. I'm not sure why, but tracing
spans weren't showing for this system. I think it's something to do with
how pipe system works, but need to investigate more. The approach in
this pr is a little better than the default span too, since it allows
adding the number of entities queried to the span which is not possible
with the default system span.
- println the number of archetype component id's that are created. This
is useful since part of the purpose of this stress test is to test how
well the use of FixedBitSet scales in the executor.

## Testing

- Ran the example with `cargo run --example many_components -F
trace_tracy 1000000` and connected with tracy
- Timed the time it took to spawn 1 million entities on main (240 s) vs
this pr (15 s)

---

## Showcase


![image](https://github.com/user-attachments/assets/69da4db0-4ecc-4acb-aebb-2e47d1a35f3b)

## Future Work

- Currently systems are created with a random set of components and
entities are created with a random set of components without any
correlation between the randomness. This means that some systems won't
match any entities and some entities could not match any systems. It
might be better to spawn the entities from the pool of components that
match the queries that the systems are using.
2025-02-10 21:13:57 +00:00
andriyDev
f17644879d
Remove labeled_assets from LoadedAsset and ErasedLoadedAsset (#15481)
# Objective

Fixes #15417.

## Solution

- Remove the `labeled_assets` fields from `LoadedAsset` and
`ErasedLoadedAsset`.
- Created new structs `CompleteLoadedAsset` and
`CompleteErasedLoadedAsset` to hold the `labeled_subassets`.
- When a subasset is `LoadContext::finish`ed, it produces a
`CompleteLoadedAsset`.
- When a `CompleteLoadedAsset` is added to a `LoadContext` (as a
subasset), their `labeled_assets` are merged, reporting any overlaps.

One important detail to note: nested subassets with overlapping names
could in theory have been used in the past for the purposes of asset
preprocessing. Even though there was no way to access these "shadowed"
nested subassets, asset preprocessing does get access to these nested
subassets. This does not seem like a case we should support though. It
is confusing at best.

## Testing

- This is just a refactor.

---

## Migration Guide

- Most uses of `LoadedAsset` and `ErasedLoadedAsset` should be replaced
with `CompleteLoadedAsset` and `CompleteErasedLoadedAsset` respectively.
2025-02-10 21:06:37 +00:00
charlotte
232824c009
Fix meshlets when bindless disabled. (#17770)
# Objective

https://github.com/bevyengine/bevy/pull/16966 tried to fix a bug where
`slot` wasn't passed to `parallaxed_uv` when not running under bindless,
but failed to account for meshlets. This surfaces on macOS where
bindless is disabled.

## Solution

Lift the slot variable out of the bindless condition so it's always
available.
2025-02-10 09:46:10 +00:00
ickshonpe
300fe4db4d
Store UI render target info locally per node (#17579)
# Objective

It's difficult to understand or make changes to the UI systems because
of how each system needs to individually track changes to scale factor,
windows and camera targets in local hashmaps, particularly for new
contributors. Any major change inevitably introduces new scale factor
bugs.

Instead of per-system resolution we can resolve the camera target info
for all UI nodes in a system at the start of `PostUpdate` and then store
it per-node in components that can be queried with change detection.

Fixes #17578
Fixes #15143

## Solution

Store the UI render target's data locally per node in a component that
is updated in `PostUpdate` before any other UI systems run.

This component can be then be queried with change detection so that UI
systems no longer need to have knowledge of cameras and windows and
don't require fragile custom change detection solutions using local
hashmaps.

## Showcase
Compare `measure_text_system` from main (which has a bug the causes it
to use the wrong scale factor when a node's camera target changes):
```
pub fn measure_text_system(
    mut scale_factors_buffer: Local<EntityHashMap<f32>>,
    mut last_scale_factors: Local<EntityHashMap<f32>>,
    fonts: Res<Assets<Font>>,
    camera_query: Query<(Entity, &Camera)>,
    default_ui_camera: DefaultUiCamera,
    ui_scale: Res<UiScale>,
    mut text_query: Query<
        (
            Entity,
            Ref<TextLayout>,
            &mut ContentSize,
            &mut TextNodeFlags,
            &mut ComputedTextBlock,
            Option<&UiTargetCamera>,
        ),
        With<Node>,
    >,
    mut text_reader: TextUiReader,
    mut text_pipeline: ResMut<TextPipeline>,
    mut font_system: ResMut<CosmicFontSystem>,
) {
    scale_factors_buffer.clear();

    let default_camera_entity = default_ui_camera.get();

    for (entity, block, content_size, text_flags, computed, maybe_camera) in &mut text_query {
        let Some(camera_entity) = maybe_camera
            .map(UiTargetCamera::entity)
            .or(default_camera_entity)
        else {
            continue;
        };
        let scale_factor = match scale_factors_buffer.entry(camera_entity) {
            Entry::Occupied(entry) => *entry.get(),
            Entry::Vacant(entry) => *entry.insert(
                camera_query
                    .get(camera_entity)
                    .ok()
                    .and_then(|(_, c)| c.target_scaling_factor())
                    .unwrap_or(1.0)
                    * ui_scale.0,
            ),
        };

        if last_scale_factors.get(&camera_entity) != Some(&scale_factor)
            || computed.needs_rerender()
            || text_flags.needs_measure_fn
            || content_size.is_added()
        {
            create_text_measure(
                entity,
                &fonts,
                scale_factor.into(),
                text_reader.iter(entity),
                block,
                &mut text_pipeline,
                content_size,
                text_flags,
                computed,
                &mut font_system,
            );
        }
    }
    core::mem::swap(&mut *last_scale_factors, &mut *scale_factors_buffer);
}
```

with `measure_text_system` from this PR (which always uses the correct
scale factor):
```
pub fn measure_text_system(
    fonts: Res<Assets<Font>>,
    mut text_query: Query<
        (
            Entity,
            Ref<TextLayout>,
            &mut ContentSize,
            &mut TextNodeFlags,
            &mut ComputedTextBlock,
            Ref<ComputedNodeTarget>,
        ),
        With<Node>,
    >,
    mut text_reader: TextUiReader,
    mut text_pipeline: ResMut<TextPipeline>,
    mut font_system: ResMut<CosmicFontSystem>,
) {
    for (entity, block, content_size, text_flags, computed, computed_target) in &mut text_query {
        // Note: the ComputedTextBlock::needs_rerender bool is cleared in create_text_measure().
        if computed_target.is_changed()
            || computed.needs_rerender()
            || text_flags.needs_measure_fn
            || content_size.is_added()
        {
            create_text_measure(
                entity,
                &fonts,
                computed_target.scale_factor.into(),
                text_reader.iter(entity),
                block,
                &mut text_pipeline,
                content_size,
                text_flags,
                computed,
                &mut font_system,
            );
        }
    }
}
```

## Testing

I removed an alarming number of tests from the `layout` module but they
were mostly to do with the deleted camera synchronisation logic. The
remaining tests should all pass now.

The most relevant examples are `multiple_windows` and `split_screen`,
the behaviour of both should be unchanged from main.

---------

Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-02-10 07:27:58 +00:00