# Objective
- Implement the foundations of automatic batching/instancing of draw
commands as the next step from #89
- NOTE: More performance improvements will come when more data is
managed and bound in ways that do not require rebinding such as mesh,
material, and texture data.
## Solution
- The core idea for batching of draw commands is to check whether any of
the information that has to be passed when encoding a draw command
changes between two things that are being drawn according to the sorted
render phase order. These should be things like the pipeline, bind
groups and their dynamic offsets, index/vertex buffers, and so on.
- The following assumptions have been made:
- Only entities with prepared assets (pipelines, materials, meshes) are
queued to phases
- View bindings are constant across a phase for a given draw function as
phases are per-view
- `batch_and_prepare_render_phase` is the only system that performs this
batching and has sole responsibility for preparing the per-object data.
As such the mesh binding and dynamic offsets are assumed to only vary as
a result of the `batch_and_prepare_render_phase` system, e.g. due to
having to split data across separate uniform bindings within the same
buffer due to the maximum uniform buffer binding size.
- Implement `GpuArrayBuffer` for `Mesh2dUniform` to store Mesh2dUniform
in arrays in GPU buffers rather than each one being at a dynamic offset
in a uniform buffer. This is the same optimisation that was made for 3D
not long ago.
- Change batch size for a range in `PhaseItem`, adding API for getting
or mutating the range. This is more flexible than a size as the length
of the range can be used in place of the size, but the start and end can
be otherwise whatever is needed.
- Add an optional mesh bind group dynamic offset to `PhaseItem`. This
avoids having to do a massive table move just to insert
`GpuArrayBufferIndex` components.
## Benchmarks
All tests have been run on an M1 Max on AC power. `bevymark` and
`many_cubes` were modified to use 1920x1080 with a scale factor of 1. I
run a script that runs a separate Tracy capture process, and then runs
the bevy example with `--features bevy_ci_testing,trace_tracy` and
`CI_TESTING_CONFIG=../benchmark.ron` with the contents of
`../benchmark.ron`:
```rust
(
exit_after: Some(1500)
)
```
...in order to run each test for 1500 frames.
The recent changes to `many_cubes` and `bevymark` added reproducible
random number generation so that with the same settings, the same rng
will occur. They also added benchmark modes that use a fixed delta time
for animations. Combined this means that the same frames should be
rendered both on main and on the branch.
The graphs compare main (yellow) to this PR (red).
### 3D Mesh `many_cubes --benchmark`
<img width="1411" alt="Screenshot 2023-09-03 at 23 42 10"
src="https://github.com/bevyengine/bevy/assets/302146/2088716a-c918-486c-8129-090b26fd2bc4">
The mesh and material are the same for all instances. This is basically
the best case for the initial batching implementation as it results in 1
draw for the ~11.7k visible meshes. It gives a ~30% reduction in median
frame time.
The 1000th frame is identical using the flip tool:
![flip many_cubes-main-mesh3d many_cubes-batching-mesh3d 67ppd
ldr](https://github.com/bevyengine/bevy/assets/302146/2511f37a-6df8-481a-932f-706ca4de7643)
```
Mean: 0.000000
Weighted median: 0.000000
1st weighted quartile: 0.000000
3rd weighted quartile: 0.000000
Min: 0.000000
Max: 0.000000
Evaluation time: 0.4615 seconds
```
### 3D Mesh `many_cubes --benchmark --material-texture-count 10`
<img width="1404" alt="Screenshot 2023-09-03 at 23 45 18"
src="https://github.com/bevyengine/bevy/assets/302146/5ee9c447-5bd2-45c6-9706-ac5ff8916daf">
This run uses 10 different materials by varying their textures. The
materials are randomly selected, and there is no sorting by material
bind group for opaque 3D so any batching is 'random'. The PR produces a
~5% reduction in median frame time. If we were to sort the opaque phase
by the material bind group, then this should be a lot faster. This
produces about 10.5k draws for the 11.7k visible entities. This makes
sense as randomly selecting from 10 materials gives a chance that two
adjacent entities randomly select the same material and can be batched.
The 1000th frame is identical in flip:
![flip many_cubes-main-mesh3d-mtc10 many_cubes-batching-mesh3d-mtc10
67ppd
ldr](https://github.com/bevyengine/bevy/assets/302146/2b3a8614-9466-4ed8-b50c-d4aa71615dbb)
```
Mean: 0.000000
Weighted median: 0.000000
1st weighted quartile: 0.000000
3rd weighted quartile: 0.000000
Min: 0.000000
Max: 0.000000
Evaluation time: 0.4537 seconds
```
### 3D Mesh `many_cubes --benchmark --vary-per-instance`
<img width="1394" alt="Screenshot 2023-09-03 at 23 48 44"
src="https://github.com/bevyengine/bevy/assets/302146/f02a816b-a444-4c18-a96a-63b5436f3b7f">
This run varies the material data per instance by randomly-generating
its colour. This is the worst case for batching and that it performs
about the same as `main` is a good thing as it demonstrates that the
batching has minimal overhead when dealing with ~11k visible mesh
entities.
The 1000th frame is identical according to flip:
![flip many_cubes-main-mesh3d-vpi many_cubes-batching-mesh3d-vpi 67ppd
ldr](https://github.com/bevyengine/bevy/assets/302146/ac5f5c14-9bda-4d1a-8219-7577d4aac68c)
```
Mean: 0.000000
Weighted median: 0.000000
1st weighted quartile: 0.000000
3rd weighted quartile: 0.000000
Min: 0.000000
Max: 0.000000
Evaluation time: 0.4568 seconds
```
### 2D Mesh `bevymark --benchmark --waves 160 --per-wave 1000 --mode
mesh2d`
<img width="1412" alt="Screenshot 2023-09-03 at 23 59 56"
src="https://github.com/bevyengine/bevy/assets/302146/cb02ae07-237b-4646-ae9f-fda4dafcbad4">
This spawns 160 waves of 1000 quad meshes that are shaded with
ColorMaterial. Each wave has a different material so 160 waves currently
should result in 160 batches. This results in a 50% reduction in median
frame time.
Capturing a screenshot of the 1000th frame main vs PR gives:
![flip bevymark-main-mesh2d bevymark-batching-mesh2d 67ppd
ldr](https://github.com/bevyengine/bevy/assets/302146/80102728-1217-4059-87af-14d05044df40)
```
Mean: 0.001222
Weighted median: 0.750432
1st weighted quartile: 0.453494
3rd weighted quartile: 0.969758
Min: 0.000000
Max: 0.990296
Evaluation time: 0.4255 seconds
```
So they seem to produce the same results. I also double-checked the
number of draws. `main` does 160000 draws, and the PR does 160, as
expected.
### 2D Mesh `bevymark --benchmark --waves 160 --per-wave 1000 --mode
mesh2d --material-texture-count 10`
<img width="1392" alt="Screenshot 2023-09-04 at 00 09 22"
src="https://github.com/bevyengine/bevy/assets/302146/4358da2e-ce32-4134-82df-3ab74c40849c">
This generates 10 textures and generates materials for each of those and
then selects one material per wave. The median frame time is reduced by
50%. Similar to the plain run above, this produces 160 draws on the PR
and 160000 on `main` and the 1000th frame is identical (ignoring the fps
counter text overlay).
![flip bevymark-main-mesh2d-mtc10 bevymark-batching-mesh2d-mtc10 67ppd
ldr](https://github.com/bevyengine/bevy/assets/302146/ebed2822-dce7-426a-858b-b77dc45b986f)
```
Mean: 0.002877
Weighted median: 0.964980
1st weighted quartile: 0.668871
3rd weighted quartile: 0.982749
Min: 0.000000
Max: 0.992377
Evaluation time: 0.4301 seconds
```
### 2D Mesh `bevymark --benchmark --waves 160 --per-wave 1000 --mode
mesh2d --vary-per-instance`
<img width="1396" alt="Screenshot 2023-09-04 at 00 13 53"
src="https://github.com/bevyengine/bevy/assets/302146/b2198b18-3439-47ad-919a-cdabe190facb">
This creates unique materials per instance by randomly-generating the
material's colour. This is the worst case for 2D batching. Somehow, this
PR manages a 7% reduction in median frame time. Both main and this PR
issue 160000 draws.
The 1000th frame is the same:
![flip bevymark-main-mesh2d-vpi bevymark-batching-mesh2d-vpi 67ppd
ldr](https://github.com/bevyengine/bevy/assets/302146/a2ec471c-f576-4a36-a23b-b24b22578b97)
```
Mean: 0.001214
Weighted median: 0.937499
1st weighted quartile: 0.635467
3rd weighted quartile: 0.979085
Min: 0.000000
Max: 0.988971
Evaluation time: 0.4462 seconds
```
### 2D Sprite `bevymark --benchmark --waves 160 --per-wave 1000 --mode
sprite`
<img width="1396" alt="Screenshot 2023-09-04 at 12 21 12"
src="https://github.com/bevyengine/bevy/assets/302146/8b31e915-d6be-4cac-abf5-c6a4da9c3d43">
This just spawns 160 waves of 1000 sprites. There should be and is no
notable difference between main and the PR.
### 2D Sprite `bevymark --benchmark --waves 160 --per-wave 1000 --mode
sprite --material-texture-count 10`
<img width="1389" alt="Screenshot 2023-09-04 at 12 36 08"
src="https://github.com/bevyengine/bevy/assets/302146/45fe8d6d-c901-4062-a349-3693dd044413">
This spawns the sprites selecting a texture at random per instance from
the 10 generated textures. This has no significant change vs main and
shouldn't.
### 2D Sprite `bevymark --benchmark --waves 160 --per-wave 1000 --mode
sprite --vary-per-instance`
<img width="1401" alt="Screenshot 2023-09-04 at 12 29 52"
src="https://github.com/bevyengine/bevy/assets/302146/762c5c60-352e-471f-8dbe-bbf10e24ebd6">
This sets the sprite colour as being unique per instance. This can still
all be drawn using one batch. There should be no difference but the PR
produces median frame times that are 4% higher. Investigation showed no
clear sources of cost, rather a mix of give and take that should not
happen. It seems like noise in the results.
### Summary
| Benchmark | % change in median frame time |
| ------------- | ------------- |
| many_cubes | 🟩 -30% |
| many_cubes 10 materials | 🟩 -5% |
| many_cubes unique materials | 🟩 ~0% |
| bevymark mesh2d | 🟩 -50% |
| bevymark mesh2d 10 materials | 🟩 -50% |
| bevymark mesh2d unique materials | 🟩 -7% |
| bevymark sprite | 🟥 2% |
| bevymark sprite 10 materials | 🟥 0.6% |
| bevymark sprite unique materials | 🟥 4.1% |
---
## Changelog
- Added: 2D and 3D mesh entities that share the same mesh and material
(same textures, same data) are now batched into the same draw command
for better performance.
---------
Co-authored-by: robtfm <50659922+robtfm@users.noreply.github.com>
Co-authored-by: Nicola Papale <nico@nicopap.ch>
# Objective
Fix a performance regression in the "[bevy vs
pixi](https://github.com/SUPERCILEX/bevy-vs-pixi)" benchmark.
This benchmark seems to have a slightly pathological distribution of `z`
values -- Sprites are spawned with a random `z` value with a child
sprite at `f32::EPSILON` relative to the parent.
See discussion here:
https://github.com/bevyengine/bevy/issues/8100#issuecomment-1726978633
## Solution
Use `radsort` for sorting `Transparent2d` `PhaseItem`s.
Use random `z` values in bevymark to stress the phase sort. Add an
`--ordered-z` option to `bevymark` that uses the old behavior.
## Benchmarks
mac m1 max
| benchmark | fps before | fps after | diff |
| - | - | - | - |
| bevymark --waves 120 --per-wave 1000 --random-z | 42.16 | 47.06 | 🟩
+11.6% |
| bevymark --waves 120 --per-wave 1000 | 52.50 | 52.29 | 🟥 -0.4% |
| bevymark --waves 120 --per-wave 1000 --mode mesh2d --random-z | 9.64 |
10.24 | 🟩 +6.2% |
| bevymark --waves 120 --per-wave 1000 --mode mesh2d | 15.83 | 15.59 | 🟥
-1.5% |
| bevy-vs-pixi | 39.71 | 59.88 | 🟩 +50.1% |
## Discussion
It's possible that `TransparentUi` should also change. We could probably
use `slice::sort_unstable_by_key` with the current sort key though, as
its items are always sorted and unique. I'd prefer to follow up later to
look into that.
Here's a survey of sorts used by other `PhaseItem`s
#### slice::sort_by_key
`Transparent2d`, `TransparentUi`
#### radsort
`Opaque3d`, `AlphaMask3d`, `Transparent3d`, `Opaque3dPrepass`,
`AlphaMask3dPrepass`, `Shadow`
I also tried `slice::sort_unstable_by_key` with a compound sort key
including `Entity`, but it didn't seem as promising and I didn't test it
as thoroughly.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Robert Swain <robert.swain@gmail.com>
# Objective
- When adding/removing bindings in large binding lists, git would
generate very difficult-to-read diffs
## Solution
- Move the `@group(X) @binding(Y)` into the same line as the binding
type declaration
I'm adopting this ~~child~~ PR.
# Objective
- Working with exclusive world access is not always easy: in many cases,
a standard system or three is more ergonomic to write, and more
modularly maintainable.
- For small, one-off tasks (commonly handled with scripting), running an
event-reader system incurs a small but flat overhead cost and muddies
the schedule.
- Certain forms of logic (e.g. turn-based games) want very fine-grained
linear and/or branching control over logic.
- SystemState is not automatically cached, and so performance can suffer
and change detection breaks.
- Fixes https://github.com/bevyengine/bevy/issues/2192.
- Partial workaround for https://github.com/bevyengine/bevy/issues/279.
## Solution
- Adds a SystemRegistry resource to the World, which stores initialized
systems keyed by their SystemSet.
- Allows users to call world.run_system(my_system) and
commands.run_system(my_system), without re-initializing or losing state
(essential for change detection).
- Add a Callback type to enable convenient use of dynamic one shot
systems and reduce the mental overhead of working with Box<dyn
SystemSet>.
- Allow users to run systems based on their SystemSet, enabling more
complex user-made abstractions.
## Future work
- Parameterized one-shot systems would improve reusability and bring
them closer to events and commands. The API could be something like
run_system_with_input(my_system, my_input) and use the In SystemParam.
- We should evaluate the unification of commands and one-shot systems
since they are two different ways to run logic on demand over a World.
### Prior attempts
- https://github.com/bevyengine/bevy/pull/2234
- https://github.com/bevyengine/bevy/pull/2417
- https://github.com/bevyengine/bevy/pull/4090
- https://github.com/bevyengine/bevy/pull/7999
This PR continues the work done in
https://github.com/bevyengine/bevy/pull/7999.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Federico Rinaldi <gisquerin@gmail.com>
Co-authored-by: MinerSebas <66798382+MinerSebas@users.noreply.github.com>
Co-authored-by: Aevyrie <aevyrie@gmail.com>
Co-authored-by: Alejandro Pascual Pozo <alejandro.pascual.pozo@gmail.com>
Co-authored-by: Rob Parrett <robparrett@gmail.com>
Co-authored-by: François <mockersf@gmail.com>
Co-authored-by: Dmytro Banin <banind@cs.washington.edu>
Co-authored-by: James Liu <contact@jamessliu.com>
# Objective
Make `bevy_ui` "root" nodes more intuitive to use/style by:
- Removing the implicit flexbox styling (such as stretch alignment) that
is applied to them, and replacing it with more intuitive CSS Grid
styling (notably with stretch alignment disabled in both axes).
- Making root nodes layout independently of each other. Instead of there
being a single implicit "viewport" node that all root nodes are children
of, there is now an implicit "viewport" node *per root node*. And layout
of each tree is computed separately.
## Solution
- Remove the global implicit viewport node, and instead create an
implicit viewport node for each user-specified root node.
- Keep track of both the user-specified root nodes and the implicit
viewport nodes in a separate `Vec`.
- Use the window's size as the `available_space` parameter to
`Taffy.compute_layout` rather than setting it on the implicit viewport
node (and set the viewport to `height: 100%; width: 100%` to make this
"just work").
---
## Changelog
- Bevy UI now lays out root nodes independently of each other in
separate layout contexts.
- The implicit viewport node (which contains each user-specified root
node) is now `Display::Grid` with `align_items` and `justify_items` both
set to `Start`.
## Migration Guide
- Bevy UI now lays out root nodes independently of each other in
separate layout contexts. If you were relying on your root nodes being
able to affect each other's layouts, then you may need to wrap them in a
single root node.
- The implicit viewport node (which contains each user-specified root
node) is now `Display::Grid` with `align_items` and `justify_items` both
set to `Start`. You may need to add `height: Val::Percent(100.)` to your
root nodes if you were previously relying on being implicitly set.
# Objective
Make the output of this example repeatable so it can be utilized by
automated screenshot diffing.
## Solution
- Use a seeded RNG for the random colors
- Offset the meshes slightly in z so they don't intersect each other at
the extents of their animations.
# Objective
Fix these
```
-- rotate_cube and move_cube
conflict on: ["bevy_transform::components::transform::Transform", "transform::CubeState"]
-- rotate_cube and scale_down_sphere_proportional_to_cube_travel_distance
conflict on: ["bevy_transform::components::transform::Transform", "transform::CubeState"]
-- move_cube and scale_down_sphere_proportional_to_cube_travel_distance
conflict on: ["bevy_transform::components::transform::Transform", "transform::CubeState"]
```
The three systems in this example depend on the results of the others.
This leads to minor but detectable differences in output between runs by
automated screenshot diffing depending on the order of the schedule.
We don't necessarily need to be able to do this for **every** example,
but I think this is a case where fixing it is easy / maybe the right
thing to do anyway.
## Solution
Chain the three systems
# Objective
Rename RemovedComponents::iter/iter_with_id to read/read_with_id to make
it clear that it consume the data
Fixes#9755.
(It's my first pull request, if i've made any mistake, please let me
know)
## Solution
Refactor RemovedComponents::iter/iter_with_id to read/read_with_id
## Changelog
Refactor RemovedComponents::iter/iter_with_id to read/read_with_id
Deprecate RemovedComponents::iter/iter_with_id
Remove IntoIterator implementation
Update removal_detection example accordingly
---
## Migration Guide
Rename calls of RemovedComponents::iter/iter_with_id to
read/read_with_id
Replace IntoIterator iteration (&mut <RemovedComponents>) with .read()
---------
Co-authored-by: denshi_ika <mojang2824@gmail.com>
# Objective
I noticed this while testing #9733.
It's not causing any problems, but we shouldn't teach users to scale 2d
stuff in z.
## Solution
Only scale in x and y.
# Objective
- Compile all targets without warnings about unused doc comments.
## Solution
- Turn unused doc comments into regular comments. Doc comments aren't
supported on expressions, so they can just be regular comments.
Comment references non existent `FromResources` instead of `FromWorld`
---------
Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: François <mockersf@gmail.com>
# Objective
Implement `From<String>` and `From<&str>` for `TextSection`
Example from something I was working on earlier:
```rust
parent.spawn(TextBundle::from_sections([
TextSection::new("press ".to_string(), TextStyle::default()),
TextSection::new("space".to_string(), TextStyle { color: Color::YELLOW, ..default() }),
TextSection::new(" to advance frames".to_string(), TextStyle::default()),
]));
```
After an `impl From<&str> for TextSection` :
```rust
parent.spawn(TextBundle::from_sections([
"press ".into(),
TextSection::new("space".to_string(), TextStyle { color: Color::YELLOW, ..default() }),
" to advance frames".into(),
]));
```
* Potentially unhelpful without a default font, so behind the
`default_font` feature.
Co-authored-by: [hate](https://github.com/hate)
---------
Co-authored-by: hate <15314665+hate@users.noreply.github.com>
# Objective
The `AssetServer` and `AssetProcessor` do a lot of `AssetPath` cloning
(across many threads). To store the path on the handle, to store paths
in dependency lists, to pass an owned path to the offloaded thread, to
pass a path to the LoadContext, etc , etc. Cloning multiple string
allocations multiple times like this will add up. It is worth optimizing
this.
Referenced in #9714
## Solution
Added a new `CowArc<T>` type to `bevy_util`, which behaves a lot like
`Cow<T>`, but the Owned variant is an `Arc<T>`. Use this in place of
`Cow<str>` and `Cow<Path>` on `AssetPath`.
---
## Changelog
- `AssetPath` now internally uses `CowArc`, making clone operations much
cheaper
- `AssetPath` now serializes as `AssetPath("some_path.extension#Label")`
instead of as `AssetPath { path: "some_path.extension", label:
Some("Label) }`
## Migration Guide
```rust
// Old
AssetPath::new("logo.png", None);
// New
AssetPath::new("logo.png");
// Old
AssetPath::new("scene.gltf", Some("Mesh0");
// New
AssetPath::new("scene.gltf").with_label("Mesh0");
```
`AssetPath` now serializes as `AssetPath("some_path.extension#Label")`
instead of as `AssetPath { path: "some_path.extension", label:
Some("Label) }`
---------
Co-authored-by: Pascal Hertleif <killercup@gmail.com>
# Objective
`many_buttons` enhancements:
* use `argh` to manage the commandline arguments like the other stress
tests
* add an option to set the number of buttons
* add a grid layout option
* centre the grid properly
* use viewport coords for the layout's style constraints
* replace use of absolute positioning
includes the changes from #9636
Displaying an image isn't actually about stress testing image rendering.
Without a second texture (the first is used by the text) the entire grid
will be drawn in a single batch. The extra texture used by the image
forces the renderer to break up the batches at every button displaying
an image, where it has to switch between the font atlas texture and the
image texture.
## Solution
<img width="401" alt="many_buttons_new"
src="https://github.com/bevyengine/bevy/assets/27962798/82140c6d-d72c-4e4f-b9b6-dd204176e51d">
---
## Changelog
`many_buttons` stress test example enhancements:
* uses `argh` to the manage the commandline arguments.
* New commandline args:
- `--help` display info & list all commandline options
- `--buttons` set the number of buttons.
- `--image-freq` set the frequency of buttons displaying images
- `--grid` use a grid layout
* style constraints are specified in viewport coords insead of
percentage values
* margins and nested bundles are used to construct the layout, instead
of absolute positioning
* the button grid centered in the window, the empty gap along the bottom
and right is removed
* an image is drawn as the background to every Nth button where N is set
using the `--image-freq` commandline option.
---------
Co-authored-by: Rob Parrett <robparrett@gmail.com>
# Bevy Asset V2 Proposal
## Why Does Bevy Need A New Asset System?
Asset pipelines are a central part of the gamedev process. Bevy's
current asset system is missing a number of features that make it
non-viable for many classes of gamedev. After plenty of discussions and
[a long community feedback
period](https://github.com/bevyengine/bevy/discussions/3972), we've
identified a number missing features:
* **Asset Preprocessing**: it should be possible to "preprocess" /
"compile" / "crunch" assets at "development time" rather than when the
game starts up. This enables offloading expensive work from deployed
apps, faster asset loading, less runtime memory usage, etc.
* **Per-Asset Loader Settings**: Individual assets cannot define their
own loaders that override the defaults. Additionally, they cannot
provide per-asset settings to their loaders. This is a huge limitation,
as many asset types don't provide all information necessary for Bevy
_inside_ the asset. For example, a raw PNG image says nothing about how
it should be sampled (ex: linear vs nearest).
* **Asset `.meta` files**: assets should have configuration files stored
adjacent to the asset in question, which allows the user to configure
asset-type-specific settings. These settings should be accessible during
the pre-processing phase. Modifying a `.meta` file should trigger a
re-processing / re-load of the asset. It should be possible to configure
asset loaders from the meta file.
* **Processed Asset Hot Reloading**: Changes to processed assets (or
their dependencies) should result in re-processing them and re-loading
the results in live Bevy Apps.
* **Asset Dependency Tracking**: The current bevy_asset has no good way
to wait for asset dependencies to load. It punts this as an exercise for
consumers of the loader apis, which is unreasonable and error prone.
There should be easy, ergonomic ways to wait for assets to load and
block some logic on an asset's entire dependency tree loading.
* **Runtime Asset Loading**: it should be (optionally) possible to load
arbitrary assets dynamically at runtime. This necessitates being able to
deploy and run the asset server alongside Bevy Apps on _all platforms_.
For example, we should be able to invoke the shader compiler at runtime,
stream scenes from sources like the internet, etc. To keep deployed
binaries (and startup times) small, the runtime asset server
configuration should be configurable with different settings compared to
the "pre processor asset server".
* **Multiple Backends**: It should be possible to load assets from
arbitrary sources (filesystems, the internet, remote asset serves, etc).
* **Asset Packing**: It should be possible to deploy assets in
compressed "packs", which makes it easier and more efficient to
distribute assets with Bevy Apps.
* **Asset Handoff**: It should be possible to hold a "live" asset
handle, which correlates to runtime data, without actually holding the
asset in memory. Ex: it must be possible to hold a reference to a GPU
mesh generated from a "mesh asset" without keeping the mesh data in CPU
memory
* **Per-Platform Processed Assets**: Different platforms and app
distributions have different capabilities and requirements. Some
platforms need lower asset resolutions or different asset formats to
operate within the hardware constraints of the platform. It should be
possible to define per-platform asset processing profiles. And it should
be possible to deploy only the assets required for a given platform.
These features have architectural implications that are significant
enough to require a full rewrite. The current Bevy Asset implementation
got us this far, but it can take us no farther. This PR defines a brand
new asset system that implements most of these features, while laying
the foundations for the remaining features to be built.
## Bevy Asset V2
Here is a quick overview of the features introduced in this PR.
* **Asset Preprocessing**: Preprocess assets at development time into
more efficient (and configurable) representations
* **Dependency Aware**: Dependencies required to process an asset are
tracked. If an asset's processed dependency changes, it will be
reprocessed
* **Hot Reprocessing/Reloading**: detect changes to asset source files,
reprocess them if they have changed, and then hot-reload them in Bevy
Apps.
* **Only Process Changes**: Assets are only re-processed when their
source file (or meta file) has changed. This uses hashing and timestamps
to avoid processing assets that haven't changed.
* **Transactional and Reliable**: Uses write-ahead logging (a technique
commonly used by databases) to recover from crashes / forced-exits.
Whenever possible it avoids full-reprocessing / only uncompleted
transactions will be reprocessed. When the processor is running in
parallel with a Bevy App, processor asset writes block Bevy App asset
reads. Reading metadata + asset bytes is guaranteed to be transactional
/ correctly paired.
* **Portable / Run anywhere / Database-free**: The processor does not
rely on an in-memory database (although it uses some database techniques
for reliability). This is important because pretty much all in-memory
databases have unsupported platforms or build complications.
* **Configure Processor Defaults Per File Type**: You can say "use this
processor for all files of this type".
* **Custom Processors**: The `Processor` trait is flexible and
unopinionated. It can be implemented by downstream plugins.
* **LoadAndSave Processors**: Most asset processing scenarios can be
expressed as "run AssetLoader A, save the results using AssetSaver X,
and then load the result using AssetLoader B". For example, load this
png image using `PngImageLoader`, which produces an `Image` asset and
then save it using `CompressedImageSaver` (which also produces an
`Image` asset, but in a compressed format), which takes an `Image` asset
as input. This means if you have an `AssetLoader` for an asset, you are
already half way there! It also means that you can share AssetSavers
across multiple loaders. Because `CompressedImageSaver` accepts Bevy's
generic Image asset as input, it means you can also use it with some
future `JpegImageLoader`.
* **Loader and Saver Settings**: Asset Loaders and Savers can now define
their own settings types, which are passed in as input when an asset is
loaded / saved. Each asset can define its own settings.
* **Asset `.meta` files**: configure asset loaders, their settings,
enable/disable processing, and configure processor settings
* **Runtime Asset Dependency Tracking** Runtime asset dependencies (ex:
if an asset contains a `Handle<Image>`) are tracked by the asset server.
An event is emitted when an asset and all of its dependencies have been
loaded
* **Unprocessed Asset Loading**: Assets do not require preprocessing.
They can be loaded directly. A processed asset is just a "normal" asset
with some extra metadata. Asset Loaders don't need to know or care about
whether or not an asset was processed.
* **Async Asset IO**: Asset readers/writers use async non-blocking
interfaces. Note that because Rust doesn't yet support async traits,
there is a bit of manual Boxing / Future boilerplate. This will
hopefully be removed in the near future when Rust gets async traits.
* **Pluggable Asset Readers and Writers**: Arbitrary asset source
readers/writers are supported, both by the processor and the asset
server.
* **Better Asset Handles**
* **Single Arc Tree**: Asset Handles now use a single arc tree that
represents the lifetime of the asset. This makes their implementation
simpler, more efficient, and allows us to cheaply attach metadata to
handles. Ex: the AssetPath of a handle is now directly accessible on the
handle itself!
* **Const Typed Handles**: typed handles can be constructed in a const
context. No more weird "const untyped converted to typed at runtime"
patterns!
* **Handles and Ids are Smaller / Faster To Hash / Compare**: Typed
`Handle<T>` is now much smaller in memory and `AssetId<T>` is even
smaller.
* **Weak Handle Usage Reduction**: In general Handles are now considered
to be "strong". Bevy features that previously used "weak `Handle<T>`"
have been ported to `AssetId<T>`, which makes it statically clear that
the features do not hold strong handles (while retaining strong type
information). Currently Handle::Weak still exists, but it is very
possible that we can remove that entirely.
* **Efficient / Dense Asset Ids**: Assets now have efficient dense
runtime asset ids, which means we can avoid expensive hash lookups.
Assets are stored in Vecs instead of HashMaps. There are now typed and
untyped ids, which means we no longer need to store dynamic type
information in the ID for typed handles. "AssetPathId" (which was a
nightmare from a performance and correctness standpoint) has been
entirely removed in favor of dense ids (which are retrieved for a path
on load)
* **Direct Asset Loading, with Dependency Tracking**: Assets that are
defined at runtime can still have their dependencies tracked by the
Asset Server (ex: if you create a material at runtime, you can still
wait for its textures to load). This is accomplished via the (currently
optional) "asset dependency visitor" trait. This system can also be used
to define a set of assets to load, then wait for those assets to load.
* **Async folder loading**: Folder loading also uses this system and
immediately returns a handle to the LoadedFolder asset, which means
folder loading no longer blocks on directory traversals.
* **Improved Loader Interface**: Loaders now have a specific "top level
asset type", which makes returning the top-level asset simpler and
statically typed.
* **Basic Image Settings and Processing**: Image assets can now be
processed into the gpu-friendly Basic Universal format. The ImageLoader
now has a setting to define what format the image should be loaded as.
Note that this is just a minimal MVP ... plenty of additional work to do
here. To demo this, enable the `basis-universal` feature and turn on
asset processing.
* **Simpler Audio Play / AudioSink API**: Asset handle providers are
cloneable, which means the Audio resource can mint its own handles. This
means you can now do `let sink_handle = audio.play(music)` instead of
`let sink_handle = audio_sinks.get_handle(audio.play(music))`. Note that
this might still be replaced by
https://github.com/bevyengine/bevy/pull/8424.
**Removed Handle Casting From Engine Features**: Ex: FontAtlases no
longer use casting between handle types
## Using The New Asset System
### Normal Unprocessed Asset Loading
By default the `AssetPlugin` does not use processing. It behaves pretty
much the same way as the old system.
If you are defining a custom asset, first derive `Asset`:
```rust
#[derive(Asset)]
struct Thing {
value: String,
}
```
Initialize the asset:
```rust
app.init_asset:<Thing>()
```
Implement a new `AssetLoader` for it:
```rust
#[derive(Default)]
struct ThingLoader;
#[derive(Serialize, Deserialize, Default)]
pub struct ThingSettings {
some_setting: bool,
}
impl AssetLoader for ThingLoader {
type Asset = Thing;
type Settings = ThingSettings;
fn load<'a>(
&'a self,
reader: &'a mut Reader,
settings: &'a ThingSettings,
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<Thing, anyhow::Error>> {
Box::pin(async move {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
// convert bytes to value somehow
Ok(Thing {
value
})
})
}
fn extensions(&self) -> &[&str] {
&["thing"]
}
}
```
Note that this interface will get much cleaner once Rust gets support
for async traits. `Reader` is an async futures_io::AsyncRead. You can
stream bytes as they come in or read them all into a `Vec<u8>`,
depending on the context. You can use `let handle =
load_context.load(path)` to kick off a dependency load, retrieve a
handle, and register the dependency for the asset.
Then just register the loader in your Bevy app:
```rust
app.init_asset_loader::<ThingLoader>()
```
Now just add your `Thing` asset files into the `assets` folder and load
them like this:
```rust
fn system(asset_server: Res<AssetServer>) {
let handle = Handle<Thing> = asset_server.load("cool.thing");
}
```
You can check load states directly via the asset server:
```rust
if asset_server.load_state(&handle) == LoadState::Loaded { }
```
You can also listen for events:
```rust
fn system(mut events: EventReader<AssetEvent<Thing>>, handle: Res<SomeThingHandle>) {
for event in events.iter() {
if event.is_loaded_with_dependencies(&handle) {
}
}
}
```
Note the new `AssetEvent::LoadedWithDependencies`, which only fires when
the asset is loaded _and_ all dependencies (and their dependencies) have
loaded.
Unlike the old asset system, for a given asset path all `Handle<T>`
values point to the same underlying Arc. This means Handles can cheaply
hold more asset information, such as the AssetPath:
```rust
// prints the AssetPath of the handle
info!("{:?}", handle.path())
```
### Processed Assets
Asset processing can be enabled via the `AssetPlugin`. When developing
Bevy Apps with processed assets, do this:
```rust
app.add_plugins(DefaultPlugins.set(AssetPlugin::processed_dev()))
```
This runs the `AssetProcessor` in the background with hot-reloading. It
reads assets from the `assets` folder, processes them, and writes them
to the `.imported_assets` folder. Asset loads in the Bevy App will wait
for a processed version of the asset to become available. If an asset in
the `assets` folder changes, it will be reprocessed and hot-reloaded in
the Bevy App.
When deploying processed Bevy apps, do this:
```rust
app.add_plugins(DefaultPlugins.set(AssetPlugin::processed()))
```
This does not run the `AssetProcessor` in the background. It behaves
like `AssetPlugin::unprocessed()`, but reads assets from
`.imported_assets`.
When the `AssetProcessor` is running, it will populate sibling `.meta`
files for assets in the `assets` folder. Meta files for assets that do
not have a processor configured look like this:
```rust
(
meta_format_version: "1.0",
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
),
),
)
```
This is metadata for an image asset. For example, if you have
`assets/my_sprite.png`, this could be the metadata stored at
`assets/my_sprite.png.meta`. Meta files are totally optional. If no
metadata exists, the default settings will be used.
In short, this file says "load this asset with the ImageLoader and use
the file extension to determine the image type". This type of meta file
is supported in all AssetPlugin modes. If in `Unprocessed` mode, the
asset (with the meta settings) will be loaded directly. If in
`ProcessedDev` mode, the asset file will be copied directly to the
`.imported_assets` folder. The meta will also be copied directly to the
`.imported_assets` folder, but with one addition:
```rust
(
meta_format_version: "1.0",
processed_info: Some((
hash: 12415480888597742505,
full_hash: 14344495437905856884,
process_dependencies: [],
)),
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: FromExtension,
),
),
)
```
`processed_info` contains `hash` (a direct hash of the asset and meta
bytes), `full_hash` (a hash of `hash` and the hashes of all
`process_dependencies`), and `process_dependencies` (the `path` and
`full_hash` of every process_dependency). A "process dependency" is an
asset dependency that is _directly_ used when processing the asset.
Images do not have process dependencies, so this is empty.
When the processor is enabled, you can use the `Process` metadata
config:
```rust
(
meta_format_version: "1.0",
asset: Process(
processor: "bevy_asset::processor::process::LoadAndSave<bevy_render::texture::image_loader::ImageLoader, bevy_render::texture::compressed_image_saver::CompressedImageSaver>",
settings: (
loader_settings: (
format: FromExtension,
),
saver_settings: (
generate_mipmaps: true,
),
),
),
)
```
This configures the asset to use the `LoadAndSave` processor, which runs
an AssetLoader and feeds the result into an AssetSaver (which saves the
given Asset and defines a loader to load it with). (for terseness
LoadAndSave will likely get a shorter/friendlier type name when [Stable
Type Paths](#7184) lands). `LoadAndSave` is likely to be the most common
processor type, but arbitrary processors are supported.
`CompressedImageSaver` saves an `Image` in the Basis Universal format
and configures the ImageLoader to load it as basis universal. The
`AssetProcessor` will read this meta, run it through the LoadAndSave
processor, and write the basis-universal version of the image to
`.imported_assets`. The final metadata will look like this:
```rust
(
meta_format_version: "1.0",
processed_info: Some((
hash: 905599590923828066,
full_hash: 9948823010183819117,
process_dependencies: [],
)),
asset: Load(
loader: "bevy_render::texture::image_loader::ImageLoader",
settings: (
format: Format(Basis),
),
),
)
```
To try basis-universal processing out in Bevy examples, (for example
`sprite.rs`), change `add_plugins(DefaultPlugins)` to
`add_plugins(DefaultPlugins.set(AssetPlugin::processed_dev()))` and run
with the `basis-universal` feature enabled: `cargo run
--features=basis-universal --example sprite`.
To create a custom processor, there are two main paths:
1. Use the `LoadAndSave` processor with an existing `AssetLoader`.
Implement the `AssetSaver` trait, register the processor using
`asset_processor.register_processor::<LoadAndSave<ImageLoader,
CompressedImageSaver>>(image_saver.into())`.
2. Implement the `Process` trait directly and register it using:
`asset_processor.register_processor(thing_processor)`.
You can configure default processors for file extensions like this:
```rust
asset_processor.set_default_processor::<ThingProcessor>("thing")
```
There is one more metadata type to be aware of:
```rust
(
meta_format_version: "1.0",
asset: Ignore,
)
```
This will ignore the asset during processing / prevent it from being
written to `.imported_assets`.
The AssetProcessor stores a transaction log at `.imported_assets/log`
and uses it to gracefully recover from unexpected stops. This means you
can force-quit the processor (and Bevy Apps running the processor in
parallel) at arbitrary times!
`.imported_assets` is "local state". It should _not_ be checked into
source control. It should also be considered "read only". In practice,
you _can_ modify processed assets and processed metadata if you really
need to test something. But those modifications will not be represented
in the hashes of the assets, so the processed state will be "out of
sync" with the source assets. The processor _will not_ fix this for you.
Either revert the change after you have tested it, or delete the
processed files so they can be re-populated.
## Open Questions
There are a number of open questions to be discussed. We should decide
if they need to be addressed in this PR and if so, how we will address
them:
### Implied Dependencies vs Dependency Enumeration
There are currently two ways to populate asset dependencies:
* **Implied via AssetLoaders**: if an AssetLoader loads an asset (and
retrieves a handle), a dependency is added to the list.
* **Explicit via the optional Asset::visit_dependencies**: if
`server.load_asset(my_asset)` is called, it will call
`my_asset.visit_dependencies`, which will grab dependencies that have
been manually defined for the asset via the Asset trait impl (which can
be derived).
This means that defining explicit dependencies is optional for "loaded
assets". And the list of dependencies is always accurate because loaders
can only produce Handles if they register dependencies. If an asset was
loaded with an AssetLoader, it only uses the implied dependencies. If an
asset was created at runtime and added with
`asset_server.load_asset(MyAsset)`, it will use
`Asset::visit_dependencies`.
However this can create a behavior mismatch between loaded assets and
equivalent "created at runtime" assets if `Assets::visit_dependencies`
doesn't exactly match the dependencies produced by the AssetLoader. This
behavior mismatch can be resolved by completely removing "implied loader
dependencies" and requiring `Asset::visit_dependencies` to supply
dependency data. But this creates two problems:
* It makes defining loaded assets harder and more error prone: Devs must
remember to manually annotate asset dependencies with `#[dependency]`
when deriving `Asset`. For more complicated assets (such as scenes), the
derive likely wouldn't be sufficient and a manual `visit_dependencies`
impl would be required.
* Removes the ability to immediately kick off dependency loads: When
AssetLoaders retrieve a Handle, they also immediately kick off an asset
load for the handle, which means it can start loading in parallel
_before_ the asset finishes loading. For large assets, this could be
significant. (although this could be mitigated for processed assets if
we store dependencies in the processed meta file and load them ahead of
time)
### Eager ProcessorDev Asset Loading
I made a controversial call in the interest of fast startup times ("time
to first pixel") for the "processor dev mode configuration". When
initializing the AssetProcessor, current processed versions of unchanged
assets are yielded immediately, even if their dependencies haven't been
checked yet for reprocessing. This means that
non-current-state-of-filesystem-but-previously-valid assets might be
returned to the App first, then hot-reloaded if/when their dependencies
change and the asset is reprocessed.
Is this behavior desirable? There is largely one alternative: do not
yield an asset from the processor to the app until all of its
dependencies have been checked for changes. In some common cases (load
dependency has not changed since last run) this will increase startup
time. The main question is "by how much" and is that slower startup time
worth it in the interest of only yielding assets that are true to the
current state of the filesystem. Should this be configurable? I'm
starting to think we should only yield an asset after its (historical)
dependencies have been checked for changes + processed as necessary, but
I'm curious what you all think.
### Paths Are Currently The Only Canonical ID / Do We Want Asset UUIDs?
In this implementation AssetPaths are the only canonical asset
identifier (just like the previous Bevy Asset system and Godot). Moving
assets will result in re-scans (and currently reprocessing, although
reprocessing can easily be avoided with some changes). Asset
renames/moves will break code and assets that rely on specific paths,
unless those paths are fixed up.
Do we want / need "stable asset uuids"? Introducing them is very
possible:
1. Generate a UUID and include it in .meta files
2. Support UUID in AssetPath
3. Generate "asset indices" which are loaded on startup and map UUIDs to
paths.
4 (maybe). Consider only supporting UUIDs for processed assets so we can
generate quick-to-load indices instead of scanning meta files.
The main "pro" is that assets referencing UUIDs don't need to be
migrated when a path changes. The main "con" is that UUIDs cannot be
"lazily resolved" like paths. They need a full view of all assets to
answer the question "does this UUID exist". Which means UUIDs require
the AssetProcessor to fully finish startup scans before saying an asset
doesnt exist. And they essentially require asset pre-processing to use
in apps, because scanning all asset metadata files at runtime to resolve
a UUID is not viable for medium-to-large apps. It really requires a
pre-generated UUID index, which must be loaded before querying for
assets.
I personally think this should be investigated in a separate PR. Paths
aren't going anywhere ... _everyone_ uses filesystems (and
filesystem-like apis) to manage their asset source files. I consider
them permanent canonical asset information. Additionally, they behave
well for both processed and unprocessed asset modes. Given that Bevy is
supporting both, this feels like the right canonical ID to start with.
UUIDS (and maybe even other indexed-identifier types) can be added later
as necessary.
### Folder / File Naming Conventions
All asset processing config currently lives in the `.imported_assets`
folder. The processor transaction log is in `.imported_assets/log`.
Processed assets are added to `.imported_assets/Default`, which will
make migrating to processed asset profiles (ex: a
`.imported_assets/Mobile` profile) a non-breaking change. It also allows
us to create top-level files like `.imported_assets/log` without it
being interpreted as an asset. Meta files currently have a `.meta`
suffix. Do we like these names and conventions?
### Should the `AssetPlugin::processed_dev` configuration enable
`watch_for_changes` automatically?
Currently it does (which I think makes sense), but it does make it the
only configuration that enables watch_for_changes by default.
### Discuss on_loaded High Level Interface:
This PR includes a very rough "proof of concept" `on_loaded` system
adapter that uses the `LoadedWithDependencies` event in combination with
`asset_server.load_asset` dependency tracking to support this pattern
```rust
fn main() {
App::new()
.init_asset::<MyAssets>()
.add_systems(Update, on_loaded(create_array_texture))
.run();
}
#[derive(Asset, Clone)]
struct MyAssets {
#[dependency]
picture_of_my_cat: Handle<Image>,
#[dependency]
picture_of_my_other_cat: Handle<Image>,
}
impl FromWorld for ArrayTexture {
fn from_world(world: &mut World) -> Self {
picture_of_my_cat: server.load("meow.png"),
picture_of_my_other_cat: server.load("meeeeeeeow.png"),
}
}
fn spawn_cat(In(my_assets): In<MyAssets>, mut commands: Commands) {
commands.spawn(SpriteBundle {
texture: my_assets.picture_of_my_cat.clone(),
..default()
});
commands.spawn(SpriteBundle {
texture: my_assets.picture_of_my_other_cat.clone(),
..default()
});
}
```
The implementation is _very_ rough. And it is currently unsafe because
`bevy_ecs` doesn't expose some internals to do this safely from inside
`bevy_asset`. There are plenty of unanswered questions like:
* "do we add a Loadable" derive? (effectively automate the FromWorld
implementation above)
* Should `MyAssets` even be an Asset? (largely implemented this way
because it elegantly builds on `server.load_asset(MyAsset { .. })`
dependency tracking).
We should think hard about what our ideal API looks like (and if this is
a pattern we want to support). Not necessarily something we need to
solve in this PR. The current `on_loaded` impl should probably be
removed from this PR before merging.
## Clarifying Questions
### What about Assets as Entities?
This Bevy Asset V2 proposal implementation initially stored Assets as
ECS Entities. Instead of `AssetId<T>` + the `Assets<T>` resource it used
`Entity` as the asset id and Asset values were just ECS components.
There are plenty of compelling reasons to do this:
1. Easier to inline assets in Bevy Scenes (as they are "just" normal
entities + components)
2. More flexible queries: use the power of the ECS to filter assets (ex:
`Query<Mesh, With<Tree>>`).
3. Extensible. Users can add arbitrary component data to assets.
4. Things like "component visualization tools" work out of the box to
visualize asset data.
However Assets as Entities has a ton of caveats right now:
* We need to be able to allocate entity ids without a direct World
reference (aka rework id allocator in Entities ... i worked around this
in my prototypes by just pre allocating big chunks of entities)
* We want asset change events in addition to ECS change tracking ... how
do we populate them when mutations can come from anywhere? Do we use
Changed queries? This would require iterating over the change data for
all assets every frame. Is this acceptable or should we implement a new
"event based" component change detection option?
* Reconciling manually created assets with asset-system managed assets
has some nuance (ex: are they "loaded" / do they also have that
component metadata?)
* "how do we handle "static" / default entity handles" (ties in to the
Entity Indices discussion:
https://github.com/bevyengine/bevy/discussions/8319). This is necessary
for things like "built in" assets and default handles in things like
SpriteBundle.
* Storing asset information as a component makes it easy to "invalidate"
asset state by removing the component (or forcing modifications).
Ideally we have ways to lock this down (some combination of Rust type
privacy and ECS validation)
In practice, how we store and identify assets is a reasonably
superficial change (porting off of Assets as Entities and implementing
dedicated storage + ids took less than a day). So once we sort out the
remaining challenges the flip should be straightforward. Additionally, I
do still have "Assets as Entities" in my commit history, so we can reuse
that work. I personally think "assets as entities" is a good endgame,
but it also doesn't provide _significant_ value at the moment and it
certainly isn't ready yet with the current state of things.
### Why not Distill?
[Distill](https://github.com/amethyst/distill) is a high quality fully
featured asset system built in Rust. It is very natural to ask "why not
just use Distill?".
It is also worth calling out that for awhile, [we planned on adopting
Distill / I signed off on
it](https://github.com/bevyengine/bevy/issues/708).
However I think Bevy has a number of constraints that make Distill
adoption suboptimal:
* **Architectural Simplicity:**
* Distill's processor requires an in-memory database (lmdb) and RPC
networked API (using Cap'n Proto). Each of these introduces API
complexity that increases maintenance burden and "code grokability".
Ignoring tests, documentation, and examples, Distill has 24,237 lines of
Rust code (including generated code for RPC + database interactions). If
you ignore generated code, it has 11,499 lines.
* Bevy builds the AssetProcessor and AssetServer using pluggable
AssetReader/AssetWriter Rust traits with simple io interfaces. They do
not necessitate databases or RPC interfaces (although Readers/Writers
could use them if that is desired). Bevy Asset V2 (at the time of
writing this PR) is 5,384 lines of Rust code (ignoring tests,
documentation, and examples). Grain of salt: Distill does have more
features currently (ex: Asset Packing, GUIDS, remote-out-of-process
asset processor). I do plan to implement these features in Bevy Asset V2
and I personally highly doubt they will meaningfully close the 6115
lines-of-code gap.
* This complexity gap (which while illustrated by lines of code, is much
bigger than just that) is noteworthy to me. Bevy should be hackable and
there are pillars of Distill that are very hard to understand and
extend. This is a matter of opinion (and Bevy Asset V2 also has
complicated areas), but I think Bevy Asset V2 is much more approachable
for the average developer.
* Necessary disclaimer: counting lines of code is an extremely rough
complexity metric. Read the code and form your own opinions.
* **Optional Asset Processing:** Not all Bevy Apps (or Bevy App
developers) need / want asset preprocessing. Processing increases the
complexity of the development environment by introducing things like
meta files, imported asset storage, running processors in the
background, waiting for processing to finish, etc. Distill _requires_
preprocessing to work. With Bevy Asset V2 processing is fully opt-in.
The AssetServer isn't directly aware of asset processors at all.
AssetLoaders only care about converting bytes to runtime Assets ... they
don't know or care if the bytes were pre-processed or not. Processing is
"elegantly" (forgive my self-congratulatory phrasing) layered on top and
builds on the existing Asset system primitives.
* **Direct Filesystem Access to Processed Asset State:** Distill stores
processed assets in a database. This makes debugging / inspecting the
processed outputs harder (either requires special tooling to query the
database or they need to be "deployed" to be inspected). Bevy Asset V2,
on the other hand, stores processed assets in the filesystem (by default
... this is configurable). This makes interacting with the processed
state more natural. Note that both Godot and Unity's new asset system
store processed assets in the filesystem.
* **Portability**: Because Distill's processor uses lmdb and RPC
networking, it cannot be run on certain platforms (ex: lmdb is a
non-rust dependency that cannot run on the web, some platforms don't
support running network servers). Bevy should be able to process assets
everywhere (ex: run the Bevy Editor on the web, compile + process
shaders on mobile, etc). Distill does partially mitigate this problem by
supporting "streaming" assets via the RPC protocol, but this is not a
full solve from my perspective. And Bevy Asset V2 can (in theory) also
stream assets (without requiring RPC, although this isn't implemented
yet)
Note that I _do_ still think Distill would be a solid asset system for
Bevy. But I think the approach in this PR is a better solve for Bevy's
specific "asset system requirements".
### Doesn't async-fs just shim requests to "sync" `std::fs`? What is the
point?
"True async file io" has limited / spotty platform support. async-fs
(and the rust async ecosystem generally ... ex Tokio) currently use
async wrappers over std::fs that offload blocking requests to separate
threads. This may feel unsatisfying, but it _does_ still provide value
because it prevents our task pools from blocking on file system
operations (which would prevent progress when there are many tasks to
do, but all threads in a pool are currently blocking on file system
ops).
Additionally, using async APIs for our AssetReaders and AssetWriters
also provides value because we can later add support for "true async
file io" for platforms that support it. _And_ we can implement other
"true async io" asset backends (such as networked asset io).
## Draft TODO
- [x] Fill in missing filesystem event APIs: file removed event (which
is expressed as dangling RenameFrom events in some cases), file/folder
renamed event
- [x] Assets without loaders are not moved to the processed folder. This
breaks things like referenced `.bin` files for GLTFs. This should be
configurable per-non-asset-type.
- [x] Initial implementation of Reflect and FromReflect for Handle. The
"deserialization" parity bar is low here as this only worked with static
UUIDs in the old impl ... this is a non-trivial problem. Either we add a
Handle::AssetPath variant that gets "upgraded" to a strong handle on
scene load or we use a separate AssetRef type for Bevy scenes (which is
converted to a runtime Handle on load). This deserves its own discussion
in a different pr.
- [x] Populate read_asset_bytes hash when run by the processor (a bit of
a special case .. when run by the processor the processed meta will
contain the hash so we don't need to compute it on the spot, but we
don't want/need to read the meta when run by the main AssetServer)
- [x] Delay hot reloading: currently filesystem events are handled
immediately, which creates timing issues in some cases. For example hot
reloading images can sometimes break because the image isn't finished
writing. We should add a delay, likely similar to the [implementation in
this PR](https://github.com/bevyengine/bevy/pull/8503).
- [x] Port old platform-specific AssetIo implementations to the new
AssetReader interface (currently missing Android and web)
- [x] Resolve on_loaded unsafety (either by removing the API entirely or
removing the unsafe)
- [x] Runtime loader setting overrides
- [x] Remove remaining unwraps that should be error-handled. There are
number of TODOs here
- [x] Pretty AssetPath Display impl
- [x] Document more APIs
- [x] Resolve spurious "reloading because it has changed" events (to
repro run load_gltf with `processed_dev()`)
- [x] load_dependency hot reloading currently only works for processed
assets. If processing is disabled, load_dependency changes are not hot
reloaded.
- [x] Replace AssetInfo dependency load/fail counters with
`loading_dependencies: HashSet<UntypedAssetId>` to prevent reloads from
(potentially) breaking counters. Storing this will also enable
"dependency reloaded" events (see [Next Steps](#next-steps))
- [x] Re-add filesystem watcher cargo feature gate (currently it is not
optional)
- [ ] Migration Guide
- [ ] Changelog
## Followup TODO
- [ ] Replace "eager unchanged processed asset loading" behavior with
"don't returned unchanged processed asset until dependencies have been
checked".
- [ ] Add true `Ignore` AssetAction that does not copy the asset to the
imported_assets folder.
- [ ] Finish "live asset unloading" (ex: free up CPU asset memory after
uploading an image to the GPU), rethink RenderAssets, and port renderer
features. The `Assets` collection uses `Option<T>` for asset storage to
support its removal. (1) the Option might not actually be necessary ...
might be able to just remove from the collection entirely (2) need to
finalize removal apis
- [ ] Try replacing the "channel based" asset id recycling with
something a bit more efficient (ex: we might be able to use raw atomic
ints with some cleverness)
- [ ] Consider adding UUIDs to processed assets (scoped just to helping
identify moved assets ... not exposed to load queries ... see [Next
Steps](#next-steps))
- [ ] Store "last modified" source asset and meta timestamps in
processed meta files to enable skipping expensive hashing when the file
wasn't changed
- [ ] Fix "slow loop" handle drop fix
- [ ] Migrate to TypeName
- [x] Handle "loader preregistration". See #9429
## Next Steps
* **Configurable per-type defaults for AssetMeta**: It should be
possible to add configuration like "all png image meta should default to
using nearest sampling" (currently this hard-coded per-loader/processor
Settings::default() impls). Also see the "Folder Meta" bullet point.
* **Avoid Reprocessing on Asset Renames / Moves**: See the "canonical
asset ids" discussion in [Open Questions](#open-questions) and the
relevant bullet point in [Draft TODO](#draft-todo). Even without
canonical ids, folder renames could avoid reprocessing in some cases.
* **Multiple Asset Sources**: Expand AssetPath to support "asset source
names" and support multiple AssetReaders in the asset server (ex:
`webserver://some_path/image.png` backed by an Http webserver
AssetReader). The "default" asset reader would use normal
`some_path/image.png` paths. Ideally this works in combination with
multiple AssetWatchers for hot-reloading
* **Stable Type Names**: this pr removes the TypeUuid requirement from
assets in favor of `std::any::type_name`. This makes defining assets
easier (no need to generate a new uuid / use weird proc macro syntax).
It also makes reading meta files easier (because things have "friendly
names"). We also use type names for components in scene files. If they
are good enough for components, they are good enough for assets. And
consistency across Bevy pillars is desirable. However,
`std::any::type_name` is not guaranteed to be stable (although in
practice it is). We've developed a [stable type
path](https://github.com/bevyengine/bevy/pull/7184) to resolve this,
which should be adopted when it is ready.
* **Command Line Interface**: It should be possible to run the asset
processor in a separate process from the command line. This will also
require building a network-server-backed AssetReader to communicate
between the app and the processor. We've been planning to build a "bevy
cli" for awhile. This seems like a good excuse to build it.
* **Asset Packing**: This is largely an additive feature, so it made
sense to me to punt this until we've laid the foundations in this PR.
* **Per-Platform Processed Assets**: It should be possible to generate
assets for multiple platforms by supporting multiple "processor
profiles" per asset (ex: compress with format X on PC and Y on iOS). I
think there should probably be arbitrary "profiles" (which can be
separate from actual platforms), which are then assigned to a given
platform when generating the final asset distribution for that platform.
Ex: maybe devs want a "Mobile" profile that is shared between iOS and
Android. Or a "LowEnd" profile shared between web and mobile.
* **Versioning and Migrations**: Assets, Loaders, Savers, and Processors
need to have versions to determine if their schema is valid. If an asset
/ loader version is incompatible with the current version expected at
runtime, the processor should be able to migrate them. I think we should
try using Bevy Reflect for this, as it would allow us to load the old
version as a dynamic Reflect type without actually having the old Rust
type. It would also allow us to define "patches" to migrate between
versions (Bevy Reflect devs are currently working on patching). The
`.meta` file already has its own format version. Migrating that to new
versions should also be possible.
* **Real Copy-on-write AssetPaths**: Rust's actual Cow (clone-on-write
type) currently used by AssetPath can still result in String clones that
aren't actually necessary (cloning an Owned Cow clones the contents).
Bevy's asset system requires cloning AssetPaths in a number of places,
which result in actual clones of the internal Strings. This is not
efficient. AssetPath internals should be reworked to exhibit truer
cow-like-behavior that reduces String clones to the absolute minimum.
* **Consider processor-less processing**: In theory the AssetServer
could run processors "inline" even if the background AssetProcessor is
disabled. If we decide this is actually desirable, we could add this.
But I don't think its a priority in the short or medium term.
* **Pre-emptive dependency loading**: We could encode dependencies in
processed meta files, which could then be used by the Asset Server to
kick of dependency loads as early as possible (prior to starting the
actual asset load). Is this desirable? How much time would this save in
practice?
* **Optimize Processor With UntypedAssetIds**: The processor exclusively
uses AssetPath to identify assets currently. It might be possible to
swap these out for UntypedAssetIds in some places, which are smaller /
cheaper to hash and compare.
* **One to Many Asset Processing**: An asset source file that produces
many assets currently must be processed into a single "processed" asset
source. If labeled assets can be written separately they can each have
their own configured savers _and_ they could be loaded more granularly.
Definitely worth exploring!
* **Automatically Track "Runtime-only" Asset Dependencies**: Right now,
tracking "created at runtime" asset dependencies requires adding them
via `asset_server.load_asset(StandardMaterial::default())`. I think with
some cleverness we could also do this for
`materials.add(StandardMaterial::default())`, making tracking work
"everywhere". There are challenges here relating to change detection /
ensuring the server is made aware of dependency changes. This could be
expensive in some cases.
* **"Dependency Changed" events**: Some assets have runtime artifacts
that need to be re-generated when one of their dependencies change (ex:
regenerate a material's bind group when a Texture needs to change). We
are generating the dependency graph so we can definitely produce these
events. Buuuuut generating these events will have a cost / they could be
high frequency for some assets, so we might want this to be opt-in for
specific cases.
* **Investigate Storing More Information In Handles**: Handles can now
store arbitrary information, which makes it cheaper and easier to
access. How much should we move into them? Canonical asset load states
(via atomics)? (`handle.is_loaded()` would be very cool). Should we
store the entire asset and remove the `Assets<T>` collection?
(`Arc<RwLock<Option<Image>>>`?)
* **Support processing and loading files without extensions**: This is a
pretty arbitrary restriction and could be supported with very minimal
changes.
* **Folder Meta**: It would be nice if we could define per folder
processor configuration defaults (likely in a `.meta` or `.folder_meta`
file). Things like "default to linear filtering for all Images in this
folder".
* **Replace async_broadcast with event-listener?** This might be
approximately drop-in for some uses and it feels more light weight
* **Support Running the AssetProcessor on the Web**: Most of the hard
work is done here, but there are some easy straggling TODOs (make the
transaction log an interface instead of a direct file writer so we can
write a web storage backend, implement an AssetReader/AssetWriter that
reads/writes to something like LocalStorage).
* **Consider identifying and preventing circular dependencies**: This is
especially important for "processor dependencies", as processing will
silently never finish in these cases.
* **Built-in/Inlined Asset Hot Reloading**: This PR regresses
"built-in/inlined" asset hot reloading (previously provided by the
DebugAssetServer). I'm intentionally punting this because I think it can
be cleanly implemented with "multiple asset sources" by registering a
"debug asset source" (ex: `debug://bevy_pbr/src/render/pbr.wgsl` asset
paths) in combination with an AssetWatcher for that asset source and
support for "manually loading pats with asset bytes instead of
AssetReaders". The old DebugAssetServer was quite nasty and I'd love to
avoid that hackery going forward.
* **Investigate ways to remove double-parsing meta files**: Parsing meta
files currently involves parsing once with "minimal" versions of the
meta file to extract the type name of the loader/processor config, then
parsing again to parse the "full" meta. This is suboptimal. We should be
able to define custom deserializers that (1) assume the loader/processor
type name comes first (2) dynamically looks up the loader/processor
registrations to deserialize settings in-line (similar to components in
the bevy scene format). Another alternative: deserialize as dynamic
Reflect objects and then convert.
* **More runtime loading configuration**: Support using the Handle type
as a hint to select an asset loader (instead of relying on AssetPath
extensions)
* **More high level Processor trait implementations**: For example, it
might be worth adding support for arbitrary chains of "asset transforms"
that modify an in-memory asset representation between loading and
saving. (ex: load a Mesh, run a `subdivide_mesh` transform, followed by
a `flip_normals` transform, then save the mesh to an efficient
compressed format).
* **Bevy Scene Handle Deserialization**: (see the relevant [Draft TODO
item](#draft-todo) for context)
* **Explore High Level Load Interfaces**: See [this
discussion](#discuss-on_loaded-high-level-interface) for one prototype.
* **Asset Streaming**: It would be great if we could stream Assets (ex:
stream a long video file piece by piece)
* **ID Exchanging**: In this PR Asset Handles/AssetIds are bigger than
they need to be because they have a Uuid enum variant. If we implement
an "id exchanging" system that trades Uuids for "efficient runtime ids",
we can cut down on the size of AssetIds, making them more efficient.
This has some open design questions, such as how to spawn entities with
"default" handle values (as these wouldn't have access to the exchange
api in the current system).
* **Asset Path Fixup Tooling**: Assets that inline asset paths inside
them will break when an asset moves. The asset system provides the
functionality to detect when paths break. We should build a framework
that enables formats to define "path migrations". This is especially
important for scene files. For editor-generated files, we should also
consider using UUIDs (see other bullet point) to avoid the need to
migrate in these cases.
---------
Co-authored-by: BeastLe9enD <beastle9end@outlook.de>
Co-authored-by: Mike <mike.hsu@gmail.com>
Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
# Objective
This link became invalid after #5589.
## Solution
The docs that were being linked to still exist, but they're on `Window`
now.
This PR just updates that link.
# Objective
- In preparation for an initial 2D/3D mesh batching/instancing PR,
enhance `bevymark` to support some different test modes that enable
comparison and optimisation of performance
## Solution
- Use `argh` for command line interface options
- Use seeded `StdRng` for reproducible random number generation
- Add a mode for testing 2D meshes that includes an option to uniquely
vary the data of each material by setting a random flat colour on the
`ColorMaterial`.
- Add a way of specifying the number of different textures to use for
sprites or meshes. These are generated at the same resolution as the
Bevy bird icon, but are just random flat colours for testing.
- Add a benchmark mode that spawns all entities during setup, and
animates the entities using a fixed delta time for reproducible
animation. The initially-spawned entities are still spawned in waves and
animated as they would have been had they spawned at intervals.
---------
Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
# Objective
The `viewport_debug` example opens a window that is physically very
large. Probably larger than the screen for the majority of machines.
## Solution
Remove the custom resolution and adjust the pixel coordinates so that
everything lines up.
At the default resolution, everything is still whole numbers even
without adjusting the viewport coordinates.
# Objective
- Make `many_cubes` suitable for testing various parts of the upcoming
batching work.
## Solution
- Use `argh` for CLI.
- Default to the sphere layout as it is more useful for benchmarking.
- Add a benchmark mode that advances the camera by a fixed step to
render the same frames across runs.
- Add an option to vary the material data per-instance. The color is
randomized.
- Add an option to generate a number of textures and randomly choose one
per instance.
- Use seeded `StdRng` for deterministic random numbers.
# Objective
Fix#8267.
Fixes half of #7840.
The `ComputedVisibility` component contains two flags: hierarchy
visibility, and view visibility (whether its visible to any cameras).
Due to the modular and open-ended way that view visibility is computed,
it triggers change detection every single frame, even when the value
does not change. Since hierarchy visibility is stored in the same
component as view visibility, this means that change detection for
inherited visibility is completely broken.
At the company I work for, this has become a real issue. We are using
change detection to only re-render scenes when necessary. The broken
state of change detection for computed visibility means that we have to
to rely on the non-inherited `Visibility` component for now. This is
workable in the early stages of our project, but since we will
inevitably want to use the hierarchy, we will have to either:
1. Roll our own solution for computed visibility.
2. Fix the issue for everyone.
## Solution
Split the `ComputedVisibility` component into two: `InheritedVisibilty`
and `ViewVisibility`.
This allows change detection to behave properly for
`InheritedVisibility`.
View visiblity is still erratic, although it is less useful to be able
to detect changes
for this flavor of visibility.
Overall, this actually simplifies the API. Since the visibility system
consists of
self-explaining components, it is much easier to document the behavior
and usage.
This approach is more modular and "ECS-like" -- one could
strip out the `ViewVisibility` component entirely if it's not needed,
and rely only on inherited visibility.
---
## Changelog
- `ComputedVisibility` has been removed in favor of:
`InheritedVisibility` and `ViewVisiblity`.
## Migration Guide
The `ComputedVisibilty` component has been split into
`InheritedVisiblity` and
`ViewVisibility`. Replace any usages of
`ComputedVisibility::is_visible_in_hierarchy`
with `InheritedVisibility::get`, and replace
`ComputedVisibility::is_visible_in_view`
with `ViewVisibility::get`.
```rust
// Before:
commands.spawn(VisibilityBundle {
visibility: Visibility::Inherited,
computed_visibility: ComputedVisibility::default(),
});
// After:
commands.spawn(VisibilityBundle {
visibility: Visibility::Inherited,
inherited_visibility: InheritedVisibility::default(),
view_visibility: ViewVisibility::default(),
});
```
```rust
// Before:
fn my_system(q: Query<&ComputedVisibilty>) {
for vis in &q {
if vis.is_visible_in_hierarchy() {
// After:
fn my_system(q: Query<&InheritedVisibility>) {
for inherited_visibility in &q {
if inherited_visibility.get() {
```
```rust
// Before:
fn my_system(q: Query<&ComputedVisibilty>) {
for vis in &q {
if vis.is_visible_in_view() {
// After:
fn my_system(q: Query<&ViewVisibility>) {
for view_visibility in &q {
if view_visibility.get() {
```
```rust
// Before:
fn my_system(mut q: Query<&mut ComputedVisibilty>) {
for vis in &mut q {
vis.set_visible_in_view();
// After:
fn my_system(mut q: Query<&mut ViewVisibility>) {
for view_visibility in &mut q {
view_visibility.set();
```
---------
Co-authored-by: Robert Swain <robert.swain@gmail.com>
# Objective
- The current `EventReader::iter` has been determined to cause confusion
among new Bevy users. It was suggested by @JoJoJet to rename the method
to better clarify its usage.
- Solves #9624
## Solution
- Rename `EventReader::iter` to `EventReader::read`.
- Rename `EventReader::iter_with_id` to `EventReader::read_with_id`.
- Rename `ManualEventReader::iter` to `ManualEventReader::read`.
- Rename `ManualEventReader::iter_with_id` to
`ManualEventReader::read_with_id`.
---
## Changelog
- `EventReader::iter` has been renamed to `EventReader::read`.
- `EventReader::iter_with_id` has been renamed to
`EventReader::read_with_id`.
- `ManualEventReader::iter` has been renamed to
`ManualEventReader::read`.
- `ManualEventReader::iter_with_id` has been renamed to
`ManualEventReader::read_with_id`.
- Deprecated `EventReader::iter`
- Deprecated `EventReader::iter_with_id`
- Deprecated `ManualEventReader::iter`
- Deprecated `ManualEventReader::iter_with_id`
## Migration Guide
- Existing usages of `EventReader::iter` and `EventReader::iter_with_id`
will have to be changed to `EventReader::read` and
`EventReader::read_with_id` respectively.
- Existing usages of `ManualEventReader::iter` and
`ManualEventReader::iter_with_id` will have to be changed to
`ManualEventReader::read` and `ManualEventReader::read_with_id`
respectively.
# Objective
- Avoid using bevy_internal imports in examples.
## Solution
- Add CI to check for bevy_internal imports like suggested in
https://github.com/bevyengine/bevy/pull/9547#issuecomment-1689377999
- Fix another import
I don't know much about CI so I don't know if this is the better
approach, but I think is better than doing a pull request every time I
found this lol, any suggestion is welcome.
---------
Co-authored-by: Rob Parrett <robparrett@gmail.com>
# Objective
A Bezier curve is a curve defined by two or more control points. In the
simplest form, it's just a line. The (arguably) most common type of
Bezier curve is a cubic Bezier, defined by four control points. These
are often used in animation, etc. Bevy has a Bezier curve struct called
`Bezier`. However, this is technically a misnomer as it only represents
cubic Bezier curves.
## Solution
This PR changes the struct name to `CubicBezier` to more accurately
reflect the struct's usage. Since it's exposed in Bevy's prelude, it can
potentially collide with other `Bezier` implementations. While that
might instead be an argument for removing it from the prelude, there's
also something to be said for adding a more general `Bezier` into Bevy,
in which case we'd likely want to use the name `Bezier`. As a final
motivator, not only is the struct located in `cubic_spines.rs`, there
are also several other spline-related structs which follow the
`CubicXxx` naming convention where applicable. For example,
`CubicSegment` represents a cubic Bezier curve (with coefficients
pre-baked).
---
## Migration Guide
- Change all `Bezier` references to `CubicBezier`
# Objective
Added `AnimationPlayer` API UX improvements.
- Succestor to https://github.com/bevyengine/bevy/pull/5912
- Fixes https://github.com/bevyengine/bevy/issues/5848
_(Credits to @asafigan for filing #5848, creating the initial pull
request, and the discussion in #5912)_
## Solution
- Created `RepeatAnimation` enum to describe an animation repetition
behavior.
- Added `is_finished()`, `set_repeat()`, and `is_playback_reversed()`
methods to the animation player.
- ~~Made the animation clip optional as per the comment from #5912~~
> ~~My problem is that the default handle [used the initialize a
`PlayingAnimation`] could actually refer to an actual animation if an
AnimationClip is set for the default handle, which leads me to ask,
"Should animation_clip should be an Option?"~~
- Added an accessor for the animation clip `animation_clip()` to the
animation player.
To determine if an animation is finished, we use the number of times the
animation has completed and the repetition behavior. If the animation is
playing in reverse then `elapsed < 0.0` counts as a completion.
Otherwise, `elapsed > animation.duration` counts as a completion. This
is what I would expect, personally. If there's any ambiguity, perhaps we
could add some `AnimationCompletionBehavior`, to specify that kind of
completion behavior to use.
Update: Previously `PlayingAnimation::elapsed` was being used as the
seek time into the animation clip. This was misleading because if you
increased the speed of the animation it would also increase (or
decrease) the elapsed time. In other words, the elapsed time was not
actually the elapsed time. To solve this, we introduce
`PlayingAnimation::seek_time` to serve as the value we manipulate the
move between keyframes. Consequently, `elapsed()` now returns the actual
elapsed time, and is not effected by the animation speed. Because
`set_elapsed` was being used to manipulate the displayed keyframe, we
introduce `AnimationPlayer::seek_to` and `AnimationPlayer::replay` to
provide this functionality.
## Migration Guide
- Removed `set_elapsed`.
- Removed `stop_repeating` in favour of
`AnimationPlayer::set_repeat(RepeatAnimation::Never)`.
- Introduced `seek_to` to seek to a given timestamp inside of the
animation.
- Introduced `seek_time` accessor for the `PlayingAnimation::seek_to`.
- Introduced `AnimationPlayer::replay` to reset the `PlayingAnimation`
to a state where no time has elapsed.
---------
Co-authored-by: Hennadii Chernyshchyk <genaloner@gmail.com>
Co-authored-by: François <mockersf@gmail.com>
# Objective
Any time we wish to transform the output of a system, we currently use
system piping to do so:
```rust
my_system.pipe(|In(x)| do_something(x))
```
Unfortunately, system piping is not a zero cost abstraction. Each call
to `.pipe` requires allocating two extra access sets: one for the second
system and one for the combined accesses of both systems. This also adds
extra work to each call to `update_archetype_component_access`, which
stacks as one adds multiple layers of system piping.
## Solution
Add the `AdapterSystem` abstraction: similar to `CombinatorSystem`, this
allows you to implement a trait to generically control how a system is
run and how its inputs and outputs are processed. Unlike
`CombinatorSystem`, this does not have any overhead when computing world
accesses which makes it ideal for simple operations such as inverting or
ignoring the output of a system.
Add the extension method `.map(...)`: this is similar to `.pipe(...)`,
only it accepts a closure as an argument instead of an `In<T>` system.
```rust
my_system.map(do_something)
```
This has the added benefit of making system names less messy: a system
that ignores its output will just be called `my_system`, instead of
`Pipe(my_system, ignore)`
---
## Changelog
TODO
## Migration Guide
The `system_adapter` functions have been deprecated: use `.map` instead,
which is a lightweight alternative to `.pipe`.
```rust
// Before:
my_system.pipe(system_adapter::ignore)
my_system.pipe(system_adapter::unwrap)
my_system.pipe(system_adapter::new(T::from))
// After:
my_system.map(std::mem::drop)
my_system.map(Result::unwrap)
my_system.map(T::from)
// Before:
my_system.pipe(system_adapter::info)
my_system.pipe(system_adapter::dbg)
my_system.pipe(system_adapter::warn)
my_system.pipe(system_adapter::error)
// After:
my_system.map(bevy_utils::info)
my_system.map(bevy_utils::dbg)
my_system.map(bevy_utils::warn)
my_system.map(bevy_utils::error)
```
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This is a continuation of this PR: #8062
# Objective
- Reorder render schedule sets to allow data preparation when phase item
order is known to support improved batching
- Part of the batching/instancing etc plan from here:
https://github.com/bevyengine/bevy/issues/89#issuecomment-1379249074
- The original idea came from @inodentry and proved to be a good one.
Thanks!
- Refactor `bevy_sprite` and `bevy_ui` to take advantage of the new
ordering
## Solution
- Move `Prepare` and `PrepareFlush` after `PhaseSortFlush`
- Add a `PrepareAssets` set that runs in parallel with other systems and
sets in the render schedule.
- Put prepare_assets systems in the `PrepareAssets` set
- If explicit dependencies are needed on Mesh or Material RenderAssets
then depend on the appropriate system.
- Add `ManageViews` and `ManageViewsFlush` sets between
`ExtractCommands` and Queue
- Move `queue_mesh*_bind_group` to the Prepare stage
- Rename them to `prepare_`
- Put systems that prepare resources (buffers, textures, etc.) into a
`PrepareResources` set inside `Prepare`
- Put the `prepare_..._bind_group` systems into a `PrepareBindGroup` set
after `PrepareResources`
- Move `prepare_lights` to the `ManageViews` set
- `prepare_lights` creates views and this must happen before `Queue`
- This system needs refactoring to stop handling all responsibilities
- Gather lights, sort, and create shadow map views. Store sorted light
entities in a resource
- Remove `BatchedPhaseItem`
- Replace `batch_range` with `batch_size` representing how many items to
skip after rendering the item or to skip the item entirely if
`batch_size` is 0.
- `queue_sprites` has been split into `queue_sprites` for queueing phase
items and `prepare_sprites` for batching after the `PhaseSort`
- `PhaseItem`s are still inserted in `queue_sprites`
- After sorting adjacent compatible sprite phase items are accumulated
into `SpriteBatch` components on the first entity of each batch,
containing a range of vertex indices. The associated `PhaseItem`'s
`batch_size` is updated appropriately.
- `SpriteBatch` items are then drawn skipping over the other items in
the batch based on the value in `batch_size`
- A very similar refactor was performed on `bevy_ui`
---
## Changelog
Changed:
- Reordered and reworked render app schedule sets. The main change is
that data is extracted, queued, sorted, and then prepared when the order
of data is known.
- Refactor `bevy_sprite` and `bevy_ui` to take advantage of the
reordering.
## Migration Guide
- Assets such as materials and meshes should now be created in
`PrepareAssets` e.g. `prepare_assets<Mesh>`
- Queueing entities to `RenderPhase`s continues to be done in `Queue`
e.g. `queue_sprites`
- Preparing resources (textures, buffers, etc.) should now be done in
`PrepareResources`, e.g. `prepare_prepass_textures`,
`prepare_mesh_uniforms`
- Prepare bind groups should now be done in `PrepareBindGroups` e.g.
`prepare_mesh_bind_group`
- Any batching or instancing can now be done in `Prepare` where the
order of the phase items is known e.g. `prepare_sprites`
## Next Steps
- Introduce some generic mechanism to ensure items that can be batched
are grouped in the phase item order, currently you could easily have
`[sprite at z 0, mesh at z 0, sprite at z 0]` preventing batching.
- Investigate improved orderings for building the MeshUniform buffer
- Implementing batching across the rest of bevy
---------
Co-authored-by: Robert Swain <robert.swain@gmail.com>
Co-authored-by: robtfm <50659922+robtfm@users.noreply.github.com>
# Objective
- Fixes#9533
## Solution
* Added `Val::ZERO` as a constant which is defined as `Val::Px(0.)`.
* Added manual `PartialEq` implementation for `Val` which allows any
zero value to equal any other zero value. E.g., `Val::Px(0.) ==
Val::Percent(0.)` etc. This is technically a breaking change, as
`Val::Px(0.) == Val::Percent(0.)` now equals `true` instead of `false`
(as an example)
* Replaced instances of `Val::Px(0.)`, `Val::Percent(0.)`, etc. with
`Val::ZERO`
* Fixed `bevy_ui::layout::convert::tests::test_convert_from` test to
account for Taffy not equating `Points(0.)` and `Percent(0.)`. These
tests now use `assert_eq!(...)` instead of `assert!(matches!(...))`
which gives easier to diagnose error messages.
# Objective
[Rust 1.72.0](https://blog.rust-lang.org/2023/08/24/Rust-1.72.0.html) is
now stable.
# Notes
- `let-else` formatting has arrived!
- I chose to allow `explicit_iter_loop` due to
https://github.com/rust-lang/rust-clippy/issues/11074.
We didn't hit any of the false positives that prevent compilation, but
fixing this did produce a lot of the "symbol soup" mentioned, e.g. `for
image in &mut *image_events {`.
Happy to undo this if there's consensus the other way.
---------
Co-authored-by: François <mockersf@gmail.com>
# Objective
Fix a few issues with some of the examples:
* Root UI nodes have an implicit parent with `FlexDirection::Row` and
`AlignItems::Stretch` set. Only a width constraint is needed to fill the
viewport. Specifying ```height: Val::Percent(100.)``` is unnecessary and
can cause confusing overflow behaviour.
* The default for position and size constraint properties is
`Val::Auto`. Setting `left: Val::Auto`, `max_height: Val::Auto`, etc
does nothing.
## Solution
Delete those lines. There should be no observable differences in the
behaviours of any of the examples.
Also changed a padding setting in the `flex_layout` example to use the
`axes` helper function.
# Objective
In #9355 was added an import using bevy_internal.
This change the import to use `bevy::window` instead of a
`bevy_internal` to run the example outside of the bevy repo.
# Objective
- When spawning a window, it will be white until the GPU is ready to
draw the app. To avoid this, we can make the window invisible and then
make it visible once the gpu is ready. Unfortunately, the visible flag
is not available to users.
## Solution
- Let users change the visible flag
## Notes
This is only user controlled. It would be nice if it was done
automatically by bevy instead but I want to keep this PR simple.
# Objective
Add `GamepadButtonInput` event
Resolves#8988
## Solution
- Add `GamepadButtonInput` type
- Emit `GamepadButtonInput` events whenever `Input<GamepadButton>` is
written to
- Update example
---------
Co-authored-by: François <mockersf@gmail.com>
# Objective
Inconvenient initialization of `UiScale`
## Solution
Change `UiScale` to a tuple struct
## Migration Guide
Replace initialization of `UiScale` like ```UiScale { scale: 1.0 }```
with ```UiScale(1.0)```
# Objective
- Significantly reduce the size of MeshUniform by only including
necessary data.
## Solution
Local to world, model transforms are affine. This means they only need a
4x3 matrix to represent them.
`MeshUniform` stores the current, and previous model transforms, and the
inverse transpose of the current model transform, all as 4x4 matrices.
Instead we can store the current, and previous model transforms as 4x3
matrices, and we only need the upper-left 3x3 part of the inverse
transpose of the current model transform. This change allows us to
reduce the serialized MeshUniform size from 208 bytes to 144 bytes,
which is over a 30% saving in data to serialize, and VRAM bandwidth and
space.
## Benchmarks
On an M1 Max, running `many_cubes -- sphere`, main is in yellow, this PR
is in red:
<img width="1484" alt="Screenshot 2023-08-11 at 02 36 43"
src="https://github.com/bevyengine/bevy/assets/302146/7d99c7b3-f2bb-4004-a8d0-4c00f755cb0d">
A reduction in frame time of ~14%.
---
## Changelog
- Changed: Redefined `MeshUniform` to improve performance by using 4x3
affine transforms and reconstructing 4x4 matrices in the shader. Helper
functions were added to `bevy_pbr::mesh_functions` to unpack the data.
`affine_to_square` converts the packed 4x3 in 3x4 matrix data to a 4x4
matrix. `mat2x4_f32_to_mat3x3` converts the 3x3 in mat2x4 + f32 matrix
data back into a 3x3.
## Migration Guide
Shader code before:
```
var model = mesh[instance_index].model;
```
Shader code after:
```
#import bevy_pbr::mesh_functions affine_to_square
var model = affine_to_square(mesh[instance_index].model);
```
Addresses:
```sh
$ cargo build --release --example lighting --target wasm32-unknown-unknown --features webgl
error: none of the selected packages contains these features: webgl, did you mean: webgl2, webp?
```
# Objective
- When following the instructions for the web examples.
- Document clearly the generated file `./target/wasm_example.js`, since
it didn't appear on `git grep` (missing extension)
## Solution
- Follow the feature rename on the docs.
---------
Signed-off-by: Seb Ospina <kraige@gmail.com>
# Objective
In the `game_menu` example:
```rust
let button_icon_style = Style {
width: Val::Px(30.0),
// This takes the icons out of the flexbox flow, to be positioned exactly
position_type: PositionType::Absolute,
// The icon will be close to the left border of the button
left: Val::Px(10.0),
right: Val::Auto,
..default()
};
```
The default value for `right` is `Val::Auto` so that line is unnecessary
and can be removed.
# Objective
I found it very difficult to understand how bevy tasks work, and I
concluded that the documentation should be improved for beginners like
me.
## Solution
These changes to the documentation were written from my beginner's
perspective after
some extremely helpful explanations by nil on Discord.
I am not familiar enough with rustdoc yet; when looking at the source, I
found the documentation at the very top of `usages.rs` helpful, but I
don't know where they are rendered. They should probably be linked to
from the main `bevy_tasks` README.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Mike <mike.hsu@gmail.com>
# Objective
- I want to run the post_processing example in a new project, but I
can't because it uses bevy internal imports.
## Solution
- Change the bevy_internal imports to their respective bevy crates
imports
# Objective
In the shader prepass example, changing to the motion vector output
hides the text, because both it and the background are rendererd black.
Seems to have been caused by this commit?
71cf35ce42
## Solution
Make the text white on all outputs.
# Objective
- I forgot to update the example after the `ViewNodeRunner` was merged.
It was even partially mentioned in one of the comments.
## Solution
- Use the `ViewNodeRunner` in the post_processing example
- I also broke up a few lines that were a bit long
---------
Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
# Objective
The `post_processing` example is currently broken when run with webgl2.
```
cargo run --example post_processing --target=wasm32-unknown-unknown
```
```
wasm.js:387 panicked at 'wgpu error: Validation Error
Caused by:
In Device::create_render_pipeline
note: label = `post_process_pipeline`
In the provided shader, the type given for group 0 binding 2 has a size of 4. As the device does not support `DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED`, the type must have a size that is a multiple of 16 bytes.
```
I bisected the breakage to c7eaedd6a1.
## Solution
Add padding when using webgl2
# Objective
- Some examples crash in CI because of needing too many resources for
the windows runner
- Some examples have random results making it hard to compare
screenshots
## Solution
- `bloom_3d`: reduce the number of spheres
- `pbr`: use simpler spheres and reuse the mesh
- `tonemapping`: use simpler spheres and reuse the mesh
- `shadow_biases`: reduce the number of spheres
- `spotlight`: use a seeded rng, move more cubes in view while reducing
the total number of cubes, and reuse meshes and materials
- `external_source_external_thread`, `iter_combinations`,
`parallel_query`: use a seeded rng
Examples of errors encountered:
```
Caused by:
In Device::create_bind_group
note: label = `bloom_upsampling_bind_group`
Not enough memory left
```
```
Caused by:
In Queue::write_buffer
Parent device is lost
```
```
ERROR wgpu_core::device::life: Mapping failed Device(Lost)
```
Redo of #7590 since I messed up my branch.
# Objective
- Revise docs.
- Refactor event loop code a little bit to make it easier to follow.
## Solution
- Do the above.
---
### Migration Guide
- `UpdateMode::Reactive { max_wait: .. }` -> `UpdateMode::Reactive {
wait: .. }`
- `UpdateMode::ReactiveLowPower { max_wait: .. }` ->
`UpdateMode::ReactiveLowPower { wait: .. }`
---------
Co-authored-by: Sélène Amanita <134181069+Selene-Amanita@users.noreply.github.com>
# Objective
The documentation for the `print_when_completed` system stated that this
system would tick the `Timer` component on every entity in the scene.
This was incorrect as this system only ticks the `Timer` on entities
with the `PrintOnCompletionTimer` component.
## Solution
We suggest a modification to the documentation of this system to make it
more clear.
# Objective
My attempt at implementing #7515
## Solution
Added struct `Pitch` and implemented on it `Source` trait.
## Changelog
### Added
- File pitch.rs to bevy_audio crate
- Struct `Pitch` and type aliases for `AudioSourceBundle<Pitch>` and
`SpatialAudioSourceBundle<Pitch>`
- New example showing how to use `PitchBundle`
### Changed
- `AudioPlugin` now adds system for `Pitch` audio
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
The `QueryParIter::for_each_mut` function is required when doing
parallel iteration with mutable queries.
This results in an unfortunate stutter:
`query.par_iter_mut().par_for_each_mut()` ('mut' is repeated).
## Solution
- Make `for_each` compatible with mutable queries, and deprecate
`for_each_mut`. In order to prevent `for_each` from being called
multiple times in parallel, we take ownership of the QueryParIter.
---
## Changelog
- `QueryParIter::for_each` is now compatible with mutable queries.
`for_each_mut` has been deprecated as it is now redundant.
## Migration Guide
The method `QueryParIter::for_each_mut` has been deprecated and is no
longer functional. Use `for_each` instead, which now supports mutable
queries.
```rust
// Before:
query.par_iter_mut().for_each_mut(|x| ...);
// After:
query.par_iter_mut().for_each(|x| ...);
```
The method `QueryParIter::for_each` now takes ownership of the
`QueryParIter`, rather than taking a shared reference.
```rust
// Before:
let par_iter = my_query.par_iter().batching_strategy(my_batching_strategy);
par_iter.for_each(|x| {
// ...Do stuff with x...
par_iter.for_each(|y| {
// ...Do nested stuff with y...
});
});
// After:
my_query.par_iter().batching_strategy(my_batching_strategy).for_each(|x| {
// ...Do stuff with x...
my_query.par_iter().batching_strategy(my_batching_strategy).for_each(|y| {
// ...Do nested stuff with y...
});
});
```
# Objective
Implements #9082 but with an option to toggle minimize and close buttons
too.
## Solution
- Added an `enabled_buttons` member to the `Window` struct through which
users can enable or disable specific window control buttons.
---
## Changelog
- Added an `enabled_buttons` member to the `Window` struct through which
users can enable or disable specific window control buttons.
- Added a new system to the `window_settings` example which demonstrates
the toggling functionality.
---
## Migration guide
- Added an `enabled_buttons` member to the `Window` struct through which
users can enable or disable specific window control buttons.
# Objective
Fix typos throughout the project.
## Solution
[`typos`](https://github.com/crate-ci/typos) project was used for
scanning, but no automatic corrections were applied. I checked
everything by hand before fixing.
Most of the changes are documentation/comments corrections. Also, there
are few trivial changes to code (variable name, pub(crate) function name
and a few error/panic messages).
## Unsolved
`bevy_reflect_derive` has
[typo](1b51053f19/crates/bevy_reflect/bevy_reflect_derive/src/type_path.rs (L76))
in enum variant name that I didn't fix. Enum is `pub(crate)`, so there
shouldn't be any trouble if fixed. However, code is tightly coupled with
macro usage, so I decided to leave it for more experienced contributor
just in case.
# Objective
Improve the `bevy_audio` API to make it more user-friendly and
ECS-idiomatic. This PR is a first-pass at addressing some of the most
obvious (to me) problems. In the interest of keeping the scope small,
further improvements can be done in future PRs.
The current `bevy_audio` API is very clunky to work with, due to how it
(ab)uses bevy assets to represent audio sinks.
The user needs to write a lot of boilerplate (accessing
`Res<Assets<AudioSink>>`) and deal with a lot of cognitive overhead
(worry about strong vs. weak handles, etc.) in order to control audio
playback.
Audio playback is initiated via a centralized `Audio` resource, which
makes it difficult to keep track of many different sounds playing in a
typical game.
Further, everything carries a generic type parameter for the sound
source type, making it difficult to mix custom sound sources (such as
procedurally generated audio or unofficial formats) with regular audio
assets.
Let's fix these issues.
## Solution
Refactor `bevy_audio` to a more idiomatic ECS API. Remove the `Audio`
resource. Do everything via entities and components instead.
Audio playback data is now stored in components:
- `PlaybackSettings`, `SpatialSettings`, `Handle<AudioSource>` are now
components. The user inserts them to tell Bevy to play a sound and
configure the initial playback parameters.
- `AudioSink`, `SpatialAudioSink` are now components instead of special
magical "asset" types. They are inserted by Bevy when it actually begins
playing the sound, and can be queried for by the user in order to
control the sound during playback.
Bundles: `AudioBundle` and `SpatialAudioBundle` are available to make it
easy for users to play sounds. Spawn an entity with one of these bundles
(or insert them to a complex entity alongside other stuff) to play a
sound.
Each entity represents a sound to be played.
There is also a new "auto-despawn" feature (activated using
`PlaybackSettings`), which, if enabled, tells Bevy to despawn entities
when the sink playback finishes. This allows for "fire-and-forget" sound
playback. Users can simply
spawn entities whenever they want to play sounds and not have to worry
about leaking memory.
## Unsolved Questions
I think the current design is *fine*. I'd be happy for it to be merged.
It has some possibly-surprising usability pitfalls, but I think it is
still much better than the old `bevy_audio`. Here are some discussion
questions for things that we could further improve. I'm undecided on
these questions, which is why I didn't implement them. We should decide
which of these should be addressed in this PR, and what should be left
for future PRs. Or if they should be addressed at all.
### What happens when sounds start playing?
Currently, the audio sink components are inserted and the bundle
components are kept. Should Bevy remove the bundle components? Something
else?
The current design allows an entity to be reused for playing the same
sound with the same parameters repeatedly. This is a niche use case I'd
like to be supported, but if we have to give it up for a simpler design,
I'd be fine with that.
### What happens if users remove any of the components themselves?
As described above, currently, entities can be reused. Removing the
audio sink causes it to be "detached" (I kept the old `Drop` impl), so
the sound keeps playing. However, if the audio bundle components are not
removed, Bevy will detect this entity as a "queued" sound entity again
(has the bundle compoenents, without a sink component), just like before
playing the sound the first time, and start playing the sound again.
This behavior might be surprising? Should we do something different?
### Should mutations to `PlaybackSettings` be applied to the audio sink?
We currently do not do that. `PlaybackSettings` is just for the initial
settings when the sound starts playing. This is clearly documented.
Do we want to keep this behavior, or do we want to allow users to use
`PlaybackSettings` instead of `AudioSink`/`SpatialAudioSink` to control
sounds during playback too?
I think I prefer for them to be kept separate. It is not a bad mental
model once you understand it, and it is documented.
### Should `AudioSink` and `SpatialAudioSink` be unified into a single
component type?
They provide a similar API (via the `AudioSinkPlayback` trait) and it
might be annoying for users to have to deal with both of them. The
unification could be done using an enum that is matched on internally by
the methods. Spatial audio has extra features, so this might make it
harder to access. I think we shouldn't.
### Automatic synchronization of spatial sound properties from
Transforms?
Should Bevy automatically apply changes to Transforms to spatial audio
entities? How do we distinguish between listener and emitter? Which one
does the transform represent? Where should the other one come from?
Alternatively, leave this problem for now, and address it in a future
PR. Or do nothing, and let users deal with it, as shown in the
`spatial_audio_2d` and `spatial_audio_3d` examples.
---
## Changelog
Added:
- `AudioBundle`/`SpatialAudioBundle`, add them to entities to play
sounds.
Removed:
- The `Audio` resource.
- `AudioOutput` is no longer `pub`.
Changed:
- `AudioSink`, `SpatialAudioSink` are now components instead of assets.
## Migration Guide
// TODO: write a more detailed migration guide, after the "unsolved
questions" are answered and this PR is finalized.
Before:
```rust
/// Need to store handles somewhere
#[derive(Resource)]
struct MyMusic {
sink: Handle<AudioSink>,
}
fn play_music(
asset_server: Res<AssetServer>,
audio: Res<Audio>,
audio_sinks: Res<Assets<AudioSink>>,
mut commands: Commands,
) {
let weak_handle = audio.play_with_settings(
asset_server.load("music.ogg"),
PlaybackSettings::LOOP.with_volume(0.5),
);
// upgrade to strong handle and store it
commands.insert_resource(MyMusic {
sink: audio_sinks.get_handle(weak_handle),
});
}
fn toggle_pause_music(
audio_sinks: Res<Assets<AudioSink>>,
mymusic: Option<Res<MyMusic>>,
) {
if let Some(mymusic) = &mymusic {
if let Some(sink) = audio_sinks.get(&mymusic.sink) {
sink.toggle();
}
}
}
```
Now:
```rust
/// Marker component for our music entity
#[derive(Component)]
struct MyMusic;
fn play_music(
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
commands.spawn((
AudioBundle::from_audio_source(asset_server.load("music.ogg"))
.with_settings(PlaybackSettings::LOOP.with_volume(0.5)),
MyMusic,
));
}
fn toggle_pause_music(
// `AudioSink` will be inserted by Bevy when the audio starts playing
query_music: Query<&AudioSink, With<MyMusic>>,
) {
if let Ok(sink) = query.get_single() {
sink.toggle();
}
}
```
# Objective
Currently, `DynamicScene`s extract all components listed in the given
(or the world's) type registry. This acts as a quasi-filter of sorts.
However, it can be troublesome to use effectively and lacks decent
control.
For example, say you need to serialize only the following component over
the network:
```rust
#[derive(Reflect, Component, Default)]
#[reflect(Component)]
struct NPC {
name: Option<String>
}
```
To do this, you'd need to:
1. Create a new `AppTypeRegistry`
2. Register `NPC`
3. Register `Option<String>`
If we skip Step 3, then the entire scene might fail to serialize as
`Option<String>` requires registration.
Not only is this annoying and easy to forget, but it can leave users
with an impossible task: serializing a third-party type that contains
private types.
Generally, the third-party crate will register their private types
within a plugin so the user doesn't need to do it themselves. However,
this means we are now unable to serialize _just_ that type— we're forced
to allow everything!
## Solution
Add the `SceneFilter` enum for filtering components to extract.
This filter can be used to optionally allow or deny entire sets of
components/resources. With the `DynamicSceneBuilder`, users have more
control over how their `DynamicScene`s are built.
To only serialize a subset of components, use the `allow` method:
```rust
let scene = builder
.allow::<ComponentA>()
.allow::<ComponentB>()
.extract_entity(entity)
.build();
```
To serialize everything _but_ a subset of components, use the `deny`
method:
```rust
let scene = builder
.deny::<ComponentA>()
.deny::<ComponentB>()
.extract_entity(entity)
.build();
```
Or create a custom filter:
```rust
let components = HashSet::from([type_id]);
let filter = SceneFilter::Allowlist(components);
// let filter = SceneFilter::Denylist(components);
let scene = builder
.with_filter(Some(filter))
.extract_entity(entity)
.build();
```
Similar operations exist for resources:
<details>
<summary>View Resource Methods</summary>
To only serialize a subset of resources, use the `allow_resource`
method:
```rust
let scene = builder
.allow_resource::<ResourceA>()
.extract_resources()
.build();
```
To serialize everything _but_ a subset of resources, use the
`deny_resource` method:
```rust
let scene = builder
.deny_resource::<ResourceA>()
.extract_resources()
.build();
```
Or create a custom filter:
```rust
let resources = HashSet::from([type_id]);
let filter = SceneFilter::Allowlist(resources);
// let filter = SceneFilter::Denylist(resources);
let scene = builder
.with_resource_filter(Some(filter))
.extract_resources()
.build();
```
</details>
### Open Questions
- [x] ~~`allow` and `deny` are mutually exclusive. Currently, they
overwrite each other. Should this instead be a panic?~~ Took @soqb's
suggestion and made it so that the opposing method simply removes that
type from the list.
- [x] ~~`DynamicSceneBuilder` extracts entity data as soon as
`extract_entity`/`extract_entities` is called. Should this behavior
instead be moved to the `build` method to prevent ordering mixups (e.g.
`.allow::<Foo>().extract_entity(entity)` vs
`.extract_entity(entity).allow::<Foo>()`)? The tradeoff would be
iterating over the given entities twice: once at extraction and again at
build.~~ Based on the feedback from @Testare it sounds like it might be
better to just keep the current functionality (if anything we can open a
separate PR that adds deferred methods for extraction, so the
choice/performance hit is up to the user).
- [ ] An alternative might be to remove the filter from
`DynamicSceneBuilder` and have it as a separate parameter to the
extraction methods (either in the existing ones or as added
`extract_entity_with_filter`-type methods). Is this preferable?
- [x] ~~Should we include constructors that include common types to
allow/deny? For example, a `SceneFilter::standard_allowlist` that
includes things like `Parent` and `Children`?~~ Consensus suggests we
should. I may split this out into a followup PR, though.
- [x] ~~Should we add the ability to remove types from the filter
regardless of whether an allowlist or denylist (e.g.
`filter.remove::<Foo>()`)?~~ See the first list item
- [x] ~~Should `SceneFilter` be an enum? Would it make more sense as a
struct that contains an `is_denylist` boolean?~~ With the added
`SceneFilter::None` state (replacing the need to wrap in an `Option` or
rely on an empty `Denylist`), it seems an enum is better suited now
- [x] ~~Bikeshed: Do we like the naming convention? Should we instead
use `include`/`exclude` terminology?~~ Sounds like we're sticking with
`allow`/`deny`!
- [x] ~~Does this feature need a new example? Do we simply include it in
the existing one (maybe even as a comment?)? Should this be done in a
followup PR instead?~~ Example will be added in a followup PR
### Followup Tasks
- [ ] Add a dedicated `SceneFilter` example
- [ ] Possibly add default types to the filter (e.g. deny things like
`ComputedVisibility`, allow `Parent`, etc)
---
## Changelog
- Added the `SceneFilter` enum for filtering components and resources
when building a `DynamicScene`
- Added methods:
- `DynamicSceneBuilder::with_filter`
- `DynamicSceneBuilder::allow`
- `DynamicSceneBuilder::deny`
- `DynamicSceneBuilder::allow_all`
- `DynamicSceneBuilder::deny_all`
- `DynamicSceneBuilder::with_resource_filter`
- `DynamicSceneBuilder::allow_resource`
- `DynamicSceneBuilder::deny_resource`
- `DynamicSceneBuilder::allow_all_resources`
- `DynamicSceneBuilder::deny_all_resources`
- Removed methods:
- `DynamicSceneBuilder::from_world_with_type_registry`
- `DynamicScene::from_scene` and `DynamicScene::from_world` no longer
require an `AppTypeRegistry` reference
## Migration Guide
- `DynamicScene::from_scene` and `DynamicScene::from_world` no longer
require an `AppTypeRegistry` reference:
```rust
// OLD
let registry = world.resource::<AppTypeRegistry>();
let dynamic_scene = DynamicScene::from_world(&world, registry);
// let dynamic_scene = DynamicScene::from_scene(&scene, registry);
// NEW
let dynamic_scene = DynamicScene::from_world(&world);
// let dynamic_scene = DynamicScene::from_scene(&scene);
```
- Removed `DynamicSceneBuilder::from_world_with_type_registry`. Now the
registry is automatically taken from the given world:
```rust
// OLD
let registry = world.resource::<AppTypeRegistry>();
let builder = DynamicSceneBuilder::from_world_with_type_registry(&world,
registry);
// NEW
let builder = DynamicSceneBuilder::from_world(&world);
```
# Objective
After the UI layout is computed when the coordinates are converted back
from physical coordinates to logical coordinates the `UiScale` is
ignored. This results in a confusing situation where we have two
different systems of logical coordinates.
Example:
```rust
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, update)
.run();
}
fn setup(mut commands: Commands, mut ui_scale: ResMut<UiScale>) {
ui_scale.scale = 4.;
commands.spawn(Camera2dBundle::default());
commands.spawn(NodeBundle {
style: Style {
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
width: Val::Percent(100.),
..Default::default()
},
..Default::default()
})
.with_children(|builder| {
builder.spawn(NodeBundle {
style: Style {
width: Val::Px(100.),
height: Val::Px(100.),
..Default::default()
},
background_color: Color::MAROON.into(),
..Default::default()
}).with_children(|builder| {
builder.spawn(TextBundle::from_section("", TextStyle::default());
});
});
}
fn update(
mut text_query: Query<(&mut Text, &Parent)>,
node_query: Query<Ref<Node>>,
) {
for (mut text, parent) in text_query.iter_mut() {
let node = node_query.get(parent.get()).unwrap();
if node.is_changed() {
text.sections[0].value = format!("size: {}", node.size());
}
}
}
```
result:
![Bevy App 30_05_2023
16_54_32](https://github.com/bevyengine/bevy/assets/27962798/a5ecbf31-0a12-4669-87df-b0c32f058732)
We asked for a 100x100 UI node but the Node's size is multiplied by the
value of `UiScale` to give a logical size of 400x400.
## Solution
Divide the output physical coordinates by `UiScale` in
`ui_layout_system` and multiply the logical viewport size by `UiScale`
when creating the projection matrix for the UI's `ExtractedView` in
`extract_default_ui_camera_view`.
---
## Changelog
* The UI layout's physical coordinates are divided by both the window
scale factor and `UiScale` when converting them back to logical
coordinates. The logical size of Ui nodes now matches the values given
to their size constraints.
* Multiply the logical viewport size by `UiScale` before creating the
projection matrix for the UI's `ExtractedView` in
`extract_default_ui_camera_view`.
* In `ui_focus_system` the cursor position returned from `Window` is
divided by `UiScale`.
* Added a scale factor parameter to `Node::physical_size` and
`Node::physical_rect`.
* The example `viewport_debug` now uses a `UiScale` of 2. to ensure that
viewport coordinates are working correctly with a non-unit `UiScale`.
## Migration Guide
Physical UI coordinates are now divided by both the `UiScale` and the
window's scale factor to compute the logical sizes and positions of UI
nodes.
This ensures that UI Node size and position values, held by the `Node`
and `GlobalTransform` components, conform to the same logical coordinate
system as the style constraints from which they are derived,
irrespective of the current `scale_factor` and `UiScale`.
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
The current mobile example produces an APK of 1.5 Gb.
- Running the example on a real device takes significant time (around
one minute just to copy the file over USB to my phone).
- Default virtual devices in Android studio run out of space after the
first install. This can of course be solved/configured, but it causes
unnecessary friction.
- One impression could be, that Bevy produces bloated APKs. 1.5Gb is
even double the size of debug builds for desktop examples.
## Solution
- Strip the debug symbols of the shared libraries before they are copied
to the APK
APK size after this change: 200Mb
Copy time on my machine: ~8s
## Considered alternative
APKs built in release mode are only 50Mb in size, but require setting up
signing for the profile and compile longer.
# Objective
The setup code in `animated_fox` uses a `done` boolean to avoid running
the `play` logic repetitively.
It is a common pattern, but it just work with exactly one fox, and
misses an even more common pattern.
When a user modifies the code to try it with several foxes, they are
confused as to why it doesn't work (#8996).
## Solution
The more common pattern is to use `Added<AnimationPlayer>` as a query
filter.
This both reduces complexity and naturally extend the setup code to
handle several foxes, added at any time.
# Objective
**This implementation is based on
https://github.com/bevyengine/rfcs/pull/59.**
---
Resolves#4597
Full details and motivation can be found in the RFC, but here's a brief
summary.
`FromReflect` is a very powerful and important trait within the
reflection API. It allows Dynamic types (e.g., `DynamicList`, etc.) to
be formed into Real ones (e.g., `Vec<i32>`, etc.).
This mainly comes into play concerning deserialization, where the
reflection deserializers both return a `Box<dyn Reflect>` that almost
always contain one of these Dynamic representations of a Real type. To
convert this to our Real type, we need to use `FromReflect`.
It also sneaks up in other ways. For example, it's a required bound for
`T` in `Vec<T>` so that `Vec<T>` as a whole can be made `FromReflect`.
It's also required by all fields of an enum as it's used as part of the
`Reflect::apply` implementation.
So in other words, much like `GetTypeRegistration` and `Typed`, it is
very much a core reflection trait.
The problem is that it is not currently treated like a core trait and is
not automatically derived alongside `Reflect`. This makes using it a bit
cumbersome and easy to forget.
## Solution
Automatically derive `FromReflect` when deriving `Reflect`.
Users can then choose to opt-out if needed using the
`#[reflect(from_reflect = false)]` attribute.
```rust
#[derive(Reflect)]
struct Foo;
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct Bar;
fn test<T: FromReflect>(value: T) {}
test(Foo); // <-- OK
test(Bar); // <-- Panic! Bar does not implement trait `FromReflect`
```
#### `ReflectFromReflect`
This PR also automatically adds the `ReflectFromReflect` (introduced in
#6245) registration to the derived `GetTypeRegistration` impl— if the
type hasn't opted out of `FromReflect` of course.
<details>
<summary><h4>Improved Deserialization</h4></summary>
> **Warning**
> This section includes changes that have since been descoped from this
PR. They will likely be implemented again in a followup PR. I am mainly
leaving these details in for archival purposes, as well as for reference
when implementing this logic again.
And since we can do all the above, we might as well improve
deserialization. We can now choose to deserialize into a Dynamic type or
automatically convert it using `FromReflect` under the hood.
`[Un]TypedReflectDeserializer::new` will now perform the conversion and
return the `Box`'d Real type.
`[Un]TypedReflectDeserializer::new_dynamic` will work like what we have
now and simply return the `Box`'d Dynamic type.
```rust
// Returns the Real type
let reflect_deserializer = UntypedReflectDeserializer::new(®istry);
let mut deserializer = ron:🇩🇪:Deserializer::from_str(input)?;
let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?;
// Returns the Dynamic type
let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(®istry);
let mut deserializer = ron:🇩🇪:Deserializer::from_str(input)?;
let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?;
```
</details>
---
## Changelog
* `FromReflect` is now automatically derived within the `Reflect` derive
macro
* This includes auto-registering `ReflectFromReflect` in the derived
`GetTypeRegistration` impl
* ~~Renamed `TypedReflectDeserializer::new` and
`UntypedReflectDeserializer::new` to
`TypedReflectDeserializer::new_dynamic` and
`UntypedReflectDeserializer::new_dynamic`, respectively~~ **Descoped**
* ~~Changed `TypedReflectDeserializer::new` and
`UntypedReflectDeserializer::new` to automatically convert the
deserialized output using `FromReflect`~~ **Descoped**
## Migration Guide
* `FromReflect` is now automatically derived within the `Reflect` derive
macro. Items with both derives will need to remove the `FromReflect`
one.
```rust
// OLD
#[derive(Reflect, FromReflect)]
struct Foo;
// NEW
#[derive(Reflect)]
struct Foo;
```
If using a manual implementation of `FromReflect` and the `Reflect`
derive, users will need to opt-out of the automatic implementation.
```rust
// OLD
#[derive(Reflect)]
struct Foo;
impl FromReflect for Foo {/* ... */}
// NEW
#[derive(Reflect)]
#[reflect(from_reflect = false)]
struct Foo;
impl FromReflect for Foo {/* ... */}
```
<details>
<summary><h4>Removed Migrations</h4></summary>
> **Warning**
> This section includes changes that have since been descoped from this
PR. They will likely be implemented again in a followup PR. I am mainly
leaving these details in for archival purposes, as well as for reference
when implementing this logic again.
* The reflect deserializers now perform a `FromReflect` conversion
internally. The expected output of `TypedReflectDeserializer::new` and
`UntypedReflectDeserializer::new` is no longer a Dynamic (e.g.,
`DynamicList`), but its Real counterpart (e.g., `Vec<i32>`).
```rust
let reflect_deserializer =
UntypedReflectDeserializer::new_dynamic(®istry);
let mut deserializer = ron:🇩🇪:Deserializer::from_str(input)?;
// OLD
let output: DynamicStruct = reflect_deserializer.deserialize(&mut
deserializer)?.take()?;
// NEW
let output: SomeStruct = reflect_deserializer.deserialize(&mut
deserializer)?.take()?;
```
Alternatively, if this behavior isn't desired, use the
`TypedReflectDeserializer::new_dynamic` and
`UntypedReflectDeserializer::new_dynamic` methods instead:
```rust
// OLD
let reflect_deserializer = UntypedReflectDeserializer::new(®istry);
// NEW
let reflect_deserializer =
UntypedReflectDeserializer::new_dynamic(®istry);
```
</details>
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
operate on naga IR directly to improve handling of shader modules.
- give codespan reporting into imported modules
- allow glsl to be used from wgsl and vice-versa
the ultimate objective is to make it possible to
- provide user hooks for core shader functions (to modify light
behaviour within the standard pbr pipeline, for example)
- make automatic binding slot allocation possible
but ... since this is already big, adds some value and (i think) is at
feature parity with the existing code, i wanted to push this now.
## Solution
i made a crate called naga_oil (https://github.com/robtfm/naga_oil -
unpublished for now, could be part of bevy) which manages modules by
- building each module independantly to naga IR
- creating "header" files for each supported language, which are used to
build dependent modules/shaders
- make final shaders by combining the shader IR with the IR for imported
modules
then integrated this into bevy, replacing some of the existing shader
processing stuff. also reworked examples to reflect this.
## Migration Guide
shaders that don't use `#import` directives should work without changes.
the most notable user-facing difference is that imported
functions/variables/etc need to be qualified at point of use, and
there's no "leakage" of visible stuff into your shader scope from the
imports of your imports, so if you used things imported by your imports,
you now need to import them directly and qualify them.
the current strategy of including/'spreading' `mesh_vertex_output`
directly into a struct doesn't work any more, so these need to be
modified as per the examples (e.g. color_material.wgsl, or many others).
mesh data is assumed to be in bindgroup 2 by default, if mesh data is
bound into bindgroup 1 instead then the shader def `MESH_BINDGROUP_1`
needs to be added to the pipeline shader_defs.
# Objective
In Bevy 10.1 and before, the only way to enable text wrapping was to set
a local `Val::Px` width constraint on the text node itself.
`Val::Percent` constraints and constraints on the text node's ancestors
did nothing.
#7779 fixed those problems. But perversely displaying unwrapped text is
really difficult now, and requires users to nest each `TextBundle` in a
`NodeBundle` and apply `min_width` and `max_width` constraints. Some
constructions may even need more than one layer of nesting. I've seen
several people already who have really struggled with this when porting
their projects to main in advance of 0.11.
## Solution
Add a `NoWrap` variant to the `BreakLineOn` enum.
If `NoWrap` is set, ignore any constraints on the width for the text and
call `TextPipeline::queue_text` with a width bound of `f32::INFINITY`.
---
## Changelog
* Added a `NoWrap` variant to the `BreakLineOn` enum.
* If `NoWrap` is set, any constraints on the width for the text are
ignored and `TextPipeline::queue_text` is called with a width bound of
`f32::INFINITY`.
* Changed the `size` field of `FixedMeasure` to `pub`. This shouldn't
have been private, it was always intended to have `pub` visibility.
* Added a `with_no_wrap` method to `TextBundle`.
## Migration Guide
`bevy_text::text::BreakLineOn` has a new variant `NoWrap` that disables
text wrapping for the `Text`.
Text wrapping can also be disabled using the `with_no_wrap` method of
`TextBundle`.
`Style` flattened `size`, `min_size` and `max_size` to its root struct,
causing compilation errors.
I uncommented the code to avoid further silent error not caught by CI,
but hid the view to keep the same behaviour.
# Objective
- Fixes#4922
## Solution
- Add an example that maps a custom texture on a 3D mesh.
---
## Changelog
> Added the texture itself (confirmed with mod on discord before it
should be ok) to the assets folder, added to the README and Cargo.toml.
---------
Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Sélène Amanita <134181069+Selene-Amanita@users.noreply.github.com>
# Objective
- Add morph targets to `bevy_pbr` (closes#5756) & load them from glTF
- Supersedes #3722
- Fixes#6814
[Morph targets][1] (also known as shape interpolation, shape keys, or
blend shapes) allow animating individual vertices with fine grained
controls. This is typically used for facial expressions. By specifying
multiple poses as vertex offset, and providing a set of weight of each
pose, it is possible to define surprisingly realistic transitions
between poses. Blending between multiple poses also allow composition.
Morph targets are part of the [gltf standard][2] and are a feature of
Unity and Unreal, and babylone.js, it is only natural to implement them
in bevy.
## Solution
This implementation of morph targets uses a 3d texture where each pixel
is a component of an animated attribute. Each layer is a different
target. We use a 2d texture for each target, because the number of
attribute×components×animated vertices is expected to always exceed the
maximum pixel row size limit of webGL2. It copies fairly closely the way
skinning is implemented on the CPU side, while on the GPU side, the
shader morph target implementation is a relatively trivial detail.
We add an optional `morph_texture` to the `Mesh` struct. The
`morph_texture` is built through a method that accepts an iterator over
attribute buffers.
The `MorphWeights` component, user-accessible, controls the blend of
poses used by mesh instances (so that multiple copy of the same mesh may
have different weights), all the weights are uploaded to a uniform
buffer of 256 `f32`. We limit to 16 poses per mesh, and a total of 256
poses.
More literature:
* Old babylone.js implementation (vertex attribute-based):
https://www.eternalcoding.com/dev-log-1-morph-targets/
* Babylone.js implementation (similar to ours):
https://www.youtube.com/watch?v=LBPRmGgU0PE
* GPU gems 3:
https://developer.nvidia.com/gpugems/gpugems3/part-i-geometry/chapter-3-directx-10-blend-shapes-breaking-limits
* Development discord thread
https://discord.com/channels/691052431525675048/1083325980615114772https://user-images.githubusercontent.com/26321040/231181046-3bca2ab2-d4d9-472e-8098-639f1871ce2e.mp4https://github.com/bevyengine/bevy/assets/26321040/d2a0c544-0ef8-45cf-9f99-8c3792f5a258
## Acknowledgements
* Thanks to `storytold` for sponsoring the feature
* Thanks to `superdump` and `james7132` for guidance and help figuring
out stuff
## Future work
- Handling of less and more attributes (eg: animated uv, animated
arbitrary attributes)
- Dynamic pose allocation (so that zero-weighted poses aren't uploaded
to GPU for example, enables much more total poses)
- Better animation API, see #8357
----
## Changelog
- Add morph targets to bevy meshes
- Support up to 64 poses per mesh of individually up to 116508 vertices,
animation currently strictly limited to the position, normal and tangent
attributes.
- Load a morph target using `Mesh::set_morph_targets`
- Add `VisitMorphTargets` and `VisitMorphAttributes` traits to
`bevy_render`, this allows defining morph targets (a fairly complex and
nested data structure) through iterators (ie: single copy instead of
passing around buffers), see documentation of those traits for details
- Add `MorphWeights` component exported by `bevy_render`
- `MorphWeights` control mesh's morph target weights, blending between
various poses defined as morph targets.
- `MorphWeights` are directly inherited by direct children (single level
of hierarchy) of an entity. This allows controlling several mesh
primitives through a unique entity _as per GLTF spec_.
- Add `MorphTargetNames` component, naming each indices of loaded morph
targets.
- Load morph targets weights and buffers in `bevy_gltf`
- handle morph targets animations in `bevy_animation` (previously, it
was a `warn!` log)
- Add the `MorphStressTest.gltf` asset for morph targets testing, taken
from the glTF samples repo, CC0.
- Add morph target manipulation to `scene_viewer`
- Separate the animation code in `scene_viewer` from the rest of the
code, reducing `#[cfg(feature)]` noise
- Add the `morph_targets.rs` example to show off how to manipulate morph
targets, loading `MorpStressTest.gltf`
## Migration Guide
- (very specialized, unlikely to be touched by 3rd parties)
- `MeshPipeline` now has a single `mesh_layouts` field rather than
separate `mesh_layout` and `skinned_mesh_layout` fields. You should
handle all possible mesh bind group layouts in your implementation
- You should also handle properly the new `MORPH_TARGETS` shader def and
mesh pipeline key. A new function is exposed to make this easier:
`setup_moprh_and_skinning_defs`
- The `MeshBindGroup` is now `MeshBindGroups`, cached bind groups are
now accessed through the `get` method.
[1]: https://en.wikipedia.org/wiki/Morph_target_animation
[2]:
https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#morph-targets
---------
Co-authored-by: François <mockersf@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
- Better consistency with `add_systems`.
- Deprecating `add_plugin` in favor of a more powerful `add_plugins`.
- Allow passing `Plugin` to `add_plugins`.
- Allow passing tuples to `add_plugins`.
## Solution
- `App::add_plugins` now takes an `impl Plugins` parameter.
- `App::add_plugin` is deprecated.
- `Plugins` is a new sealed trait that is only implemented for `Plugin`,
`PluginGroup` and tuples over `Plugins`.
- All examples, benchmarks and tests are changed to use `add_plugins`,
using tuples where appropriate.
---
## Changelog
### Changed
- `App::add_plugins` now accepts all types that implement `Plugins`,
which is implemented for:
- Types that implement `Plugin`.
- Types that implement `PluginGroup`.
- Tuples (up to 16 elements) over types that implement `Plugins`.
- Deprecated `App::add_plugin` in favor of `App::add_plugins`.
## Migration Guide
- Replace `app.add_plugin(plugin)` calls with `app.add_plugins(plugin)`.
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
Fixes#6920
## Solution
From the issue discussion:
> From looking at the `AsBindGroup` derive macro implementation, the
fallback image's `TextureView` is used when the binding's
`Option<Handle<Image>>` is `None`. Because this relies on already having
a view that matches the desired binding dimensions, I think the solution
will require creating a separate `GpuImage` for each possible
`TextureViewDimension`.
---
## Changelog
Users can now rely on `FallbackImage` to work with a texture binding of
any dimension.
# Objective
This adds support for using texture atlas sprites in UI. From
discussions today in the ui-dev discord it seems this is a much wanted
feature.
This was previously attempted in #5070 by @ManevilleF however that was
blocked #5103. This work can be easily modified to support #5103 changes
after that merges.
## Solution
I created a new UI bundle that reuses the existing texture atlas
infrastructure. I create a new atlas image component to prevent it from
being drawn by the existing non-UI systems and to remove unused
parameters.
In extract I added new system to calculate the required values for the
texture atlas image, this extracts into the same resource as the
existing UI Image and Text components.
This should have minimal performance impact because if texture atlas is
not present then the exact same code path is followed. Also there should
be no unintended behavior changes because without the new components the
existing systems write the extract same resulting data.
I also added an example showing the sprite working and a system to
advance the animation on space bar presses.
Naming is hard and I would accept any feedback on the bundle name!
---
## Changelog
> Added TextureAtlasImageBundle
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
# Objective
Implement borders for UI nodes.
Relevant discussion: #7785
Related: #5924, #3991
<img width="283" alt="borders"
src="https://user-images.githubusercontent.com/27962798/220968899-7661d5ec-6f5b-4b0f-af29-bf9af02259b5.PNG">
## Solution
Add an extraction function to draw the borders.
---
Can only do one colour rectangular borders due to the limitations of the
Bevy UI renderer.
Maybe it can be combined with #3991 eventually to add curved border
support.
## Changelog
* Added a component `BorderColor`.
* Added the `extract_uinode_borders` system to the UI Render App.
* Added the UI example `borders`
---------
Co-authored-by: Nico Burns <nico@nicoburns.com>
# Objective
The AccessKit PR removed the loading of the image logo from the UI
example.
It also added some alt text with `TextStyle::default()` as a child of
the logo image node.
If you give an image node a child, then its size is no longer determined
by the measurefunc that preserves its aspect ratio. Instead, its width
and height are determined by the constraints set on the node and the
size of the contents of the node. In this case, the image node is set to
have a width of 500 with no constraints on its height. So it looks at
its child node to determine what height it should take. Because the
child has `TextStyle::default` it allocates no space for the text, the
height of the image node is set to zero and the logo isn't drawn.
Fixes#8805
## Solution
Load the image, and set min_size and max_size constraints of 500 by 125
pixels.
# Objective
The goal of this PR is to receive touchpad magnification and rotation
events.
## Solution
Implement pendants for winit's `TouchpadMagnify` and `TouchpadRotate`
events.
Adjust the `mouse_input_events.rs` example to debug magnify and rotate
events.
Since winit only reports these events on macOS, the Bevy events for
touchpad magnification and rotation are currently only fired on macOS.
# Objective
Be consistent with `Resource`s and `Components` and have `Event` types
be more self-documenting.
Although not susceptible to accidentally using a function instead of a
value due to `Event`s only being initialized by their type, much of the
same reasoning for removing the blanket impl on `Resource` also applies
here.
* Not immediately obvious if a type is intended to be an event
* Prevent invisible conflicts if the same third-party or primitive types
are used as events
* Allows for further extensions (e.g. opt-in warning for missed events)
## Solution
Remove the blanket impl for the `Event` trait. Add a derive macro for
it.
---
## Changelog
- `Event` is no longer implemented for all applicable types. Add the
`#[derive(Event)]` macro for events.
## Migration Guide
* Add the `#[derive(Event)]` macro for events. Third-party types used as
events should be wrapped in a newtype.
# Objective
- Fixes https://github.com/bevyengine/bevy/issues/8586.
## Solution
- Add `preferred_theme` field to `Window` and set it when window
creation
- Add `window_theme` field to `InternalWindowState` to store current
window theme
- Expose winit `WindowThemeChanged` event
---------
Co-authored-by: hate <15314665+hate@users.noreply.github.com>
Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: François <mockersf@gmail.com>
# Objective
I was trying to add some `Diagnostics` to have a better break down of
performance but I noticed that the current implementation uses a
`ResMut` which forces the functions to all run sequentially whereas
before they could run in parallel. This created too great a performance
penalty to be usable.
## Solution
This PR reworks how the diagnostics work with a couple of breaking
changes. The idea is to change how `Diagnostics` works by changing it to
a `SystemParam`. This allows us to hold a `Deferred` buffer of
measurements that can be applied later, avoiding the need for multiple
mutable references to the hashmap. This means we can run systems that
write diagnostic measurements in parallel.
Firstly, we rename the old `Diagnostics` to `DiagnosticsStore`. This
clears up the original name for the new interface while allowing us to
preserve more closely the original API.
Then we create a new `Diagnostics` struct which implements `SystemParam`
and contains a deferred `SystemBuffer`. This can be used very similar to
the old `Diagnostics` for writing new measurements.
```rust
fn system(diagnostics: ResMut<Diagnostics>) { diagnostics.new_measurement(ID, || 10.0)}
// changes to
fn system(mut diagnostics: Diagnostics) { diagnostics.new_measurement(ID, || 10.0)}
```
For reading the diagnostics, the user needs to change from `Diagnostics`
to `DiagnosticsStore` but otherwise the function calls are the same.
Finally, we add a new method to the `App` for registering diagnostics.
This replaces the old method of creating a startup system and adding it
manually.
Testing it, this PR does indeed allow Diagnostic systems to be run in
parallel.
## Changelog
- Change `Diagnostics` to implement `SystemParam` which allows
diagnostic systems to run in parallel.
## Migration Guide
- Register `Diagnostic`'s using the new
`app.register_diagnostic(Diagnostic::new(DIAGNOSTIC_ID,
"diagnostic_name", 10));`
- In systems for writing new measurements, change `mut diagnostics:
ResMut<Diagnostics>` to `mut diagnostics: Diagnostics` to allow the
systems to run in parallel.
- In systems for reading measurements, change `diagnostics:
Res<Diagnostics>` to `diagnostics: Res<DiagnosticsStore>`.
# Objective
- Introduce a stable alternative to
[`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html).
- Rewrite of #5805 with heavy inspiration in design.
- On the path to #5830.
- Part of solving #3327.
## Solution
- Add a `TypePath` trait for static stable type path/name information.
- Add a `TypePath` derive macro.
- Add a `impl_type_path` macro for implementing internal and foreign
types in `bevy_reflect`.
---
## Changelog
- Added `TypePath` trait.
- Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`.
- Added a `TypePath` derive macro.
- Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on
internal and foreign types in `bevy_reflect`.
- Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to
`(Non)GenericTypedCell<T>` which allows us to be generic over both
`TypeInfo` and `TypePath`.
- `TypePath` is now a supertrait of `Asset`, `Material` and
`Material2d`.
- `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be
specified.
- `impl_reflect_value` needs to either specify path starting with a
double colon (`::core::option::Option`) or an `in my_crate::foo`
declaration.
- Added `bevy_reflect_derive::ReflectTypePath`.
- Most uses of `Ident` in `bevy_reflect_derive` changed to use
`ReflectTypePath`.
## Migration Guide
- Implementors of `Asset`, `Material` and `Material2d` now also need to
derive `TypePath`.
- Manual implementors of `Reflect` will need to implement the new
`get_type_path` method.
## Open Questions
- [x] ~This PR currently does not migrate any usages of
`std::any::type_name` to use `bevy_reflect::TypePath` to ease the review
process. Should it?~ Migration will be left to a follow-up PR.
- [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to
satisfy new bounds, mostly when deriving `TypeUuid`. Should we make
`TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in
favour of
`TypePath`?](2afbd85532 (r961067892))
# Objective
- `apply_system_buffers` is an unhelpful name: it introduces a new
internal-only concept
- this is particularly rough for beginners as reasoning about how
commands work is a critical stumbling block
## Solution
- rename `apply_system_buffers` to the more descriptive `apply_deferred`
- rename related fields, arguments and methods in the internals fo
bevy_ecs for consistency
- update the docs
## Changelog
`apply_system_buffers` has been renamed to `apply_deferred`, to more
clearly communicate its intent and relation to `Deferred` system
parameters like `Commands`.
## Migration Guide
- `apply_system_buffers` has been renamed to `apply_deferred`
- the `apply_system_buffers` method on the `System` trait has been
renamed to `apply_deferred`
- the `is_apply_system_buffers` function has been replaced by
`is_apply_deferred`
- `Executor::set_apply_final_buffers` is now
`Executor::set_apply_final_deferred`
- `Schedule::apply_system_buffers` is now `Schedule::apply_deferred`
---------
Co-authored-by: JoJoJet <21144246+JoJoJet@users.noreply.github.com>