mirror of
https://github.com/bevyengine/bevy.git
synced 2026-07-02 00:33:03 -04:00
create-pull-request/patch
251 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0d00c9109f |
Fix nightly clippy and clippy::result_large_err again (#24624)
# Objective #24206 boxes the large error in `GltfError` but it's not sufficient after https://github.com/rust-lang/rust-clippy/issues/17070 is fixed. ## Solution Box `AssetLoadError::RequestedHandleTypeMismatch` so that it is reduced to 120 bytes which is below 128 bytes. Also fix some other trivial clippy warnings. ## Testing Use the latest nightly clippy which has https://github.com/rust-lang/rust-clippy/issues/17070 fixed: ``` cargo +nightly clippy --workspace --all-features --all-targets -- -D warnings ``` |
||
|
|
7517c61ecd |
Add options to lower precision/compressed vertex buffer (#21926)
# Objective Resolves #21902. ## Solution This PR adopts a relatively transparent approach to reduce the GPU vertex buffer size. On CPU-side mesh can still use uncompressed Float32 data, and users are not required to insert compressed vertex formats. The vertex data is automatically processed into lower-precision/octahedral encoded data when uploading to the GPU. To enable vertex attribute compression, just set the `attribute_compression` field of Mesh, or set `mesh_attribute_compression` of GltfLoaderSettings. If enabled, normal and tangent will be octahedral encoded Unorm16x2, uv0, uv1, joint weight and color will be corresponding Unorm16 or Float16. I also provide Unorm8x4 for vertex color if hdr isn't needed. Update 2026-2-16 Removed previous approach that automatically compresses vertex buffer according to flags when uploading to GPU. Instead, I added `compressed_mesh` method to Mesh to construct compressed Mesh ahead of time. GltfLoader can also opt-in mesh compressing when loading. I also add an option to convert indices to u16, though I believe blender gltf exporter already uses u16 indices when possible. ## Testing Run `many_cubes`, `many_foxes`, `many_morph_targets` with `--vertex-compression` to test 3d. Run `bevymark` with `sprite_mesh` to test 2d, because `SpriteMesh` uses compressed quad mesh now. --------- Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com> |
||
|
|
98639670f5 |
Add many_meshlet_materials stress test example (#23792)
# Objective
- Introduce a dedicated example to measure performance overhead when
dealing with a high number of meshlet instances and unique materials.
## Solution
- Added a new example `many_meshlet_materials` that spawns a
configurable grid of meshlet bunnies.
## Testing
- `SystemInfo { os: "Linux (CachyOS Linux rolling)", kernel:
"6.19.10-1-cachyos", cpu: "AMD Ryzen 7 5800H with Radeon Graphics",
core_count: "8", memory: "15.0 GiB" }`
- `AdapterInfo { name: "AMD Radeon Graphics (RADV RENOIR)", vendor:
4098, device: 5688, device_type: IntegratedGpu, device_pci_bus_id:
"0000:03:00.0", driver: "radv", driver_info: "Mesa 26.0.3-arch2.2",
backend: Vulkan, subgroup_min_size: 64, subgroup_max_size: 64,
transient_saves_memory: false }`
- `cargo run --features=meshlet,https --release --example
many_meshlet_materials`
---
## Showcase
Example works perfectly with 5x5 grid.
<img width="1024" height="599" alt="image"
src="https://github.com/user-attachments/assets/948b42b7-e88e-4a91-9fb7-f1c1b4d27e32"
/>
The issue begins when grid count increases to 10x10. Meshes start
flickering.
<img width="1024" height="598" alt="image"
src="https://github.com/user-attachments/assets/055df47f-39dc-41bc-be05-1978ec352e3a"
/>
With 50x50 meshes, the whole screen flickers.
<img width="1024" height="595" alt="image"
src="https://github.com/user-attachments/assets/7d685a2b-7389-43fe-b04e-a5987136f459"
/>
When running with unique materials, performance significantly drops on
my hardware.
<img width="2224" height="1298" alt="image"
src="https://github.com/user-attachments/assets/8d5b4677-7851-4234-9467-992f8db62a9d"
/>
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
|
||
|
|
649586a47d |
Add an overflow-flip argument to many_buttons (#24244)
# Objective Add an option enabling clipping on the button nodes in `many_buttons`. To help identify any performance regressions due to UI clipping changes. ## Solution Add an option that sets overflow to clip for all the button nodes in `testbed_ui`. |
||
|
|
b00ff93c87 |
Remove new_with_ prefix from the TextLayout constuctor functions (#24049)
# Objective Remove the `new_with_` prefixes from the `TextLayout` constuctor functions. Generally, the "new" part is redundant and "with" is used by fluent APIs. ## Solution Just delete the prefixes, shorten the names (all on `TextLayout`). * `new_with_justify` -> `justify` * `new_with_linebreak` -> `linebreak` * `new_with_no_wrap` -> `no_wrap` |
||
|
|
85212f763e |
Fix CI errors for Rust 1.95 (#23829)
# Objective CI is [broken](https://github.com/bevyengine/bevy/actions/runs/24511176705/job/71642514168?pr=23785) ## Solution Look at the CI errors. Fix them. ## Testing Let's see if CI is green! |
||
|
|
535cf401cc |
Reframe old "scene" terminology as "world serialization" (#23630)
Part 2 of #23619 In **Bevy 0.19** we are landing a subset of Bevy's Next Generation Scene system (often known as BSN), which now lives in the `bevy_scene` / `bevy::scene` crate. However the old `bevy_scene` system still needs to stick around for a bit longer, as it provides some features that Bevy's Next Generation Scene system doesn't (yet!): 1. It is not _yet_ possible to write a World _to_ BSN, so the old system is still necessary for "round trip World serialization". 2. The GLTF scene loader has not yet been ported to BSN, so the old system is still necessary to spawn GLTF scenes in Bevy. For this reason, we have renamed the old `bevy_scene` crate to `bevy_world_serialization`. If you were referencing `bevy_scene::*` or `bevy::scene::*` types, rename those paths to `bevy_world_serialization::*` and `bevy::world_serialization::*` respectively. Additionally, to avoid confusion / conflicts with the new scene system, all "scene" terminology / types have been reframed as "world serialization": - `Scene` -> `WorldAsset` (as this was always just a World wrapper) - `SceneRoot` -> `WorldAssetRoot` - `DynamicScene` -> `DynamicWorld` - `DynamicScene::from_scene` -> `DynamicWorld::from_world_asset` - `DynamicSceneBuilder` -> `DynamicWorldBuilder` - `DynamicSceneRoot` -> `DynamicWorldRoot` - `SceneInstanceReady` -> `WorldInstanceReady` - `SceneLoader` -> `WorldAssetLoader` - `ScenePlugin` -> `WorldSerializationPlugin` - `SceneRootTemplate` -> `WorldAssetRootTemplate` - `SceneSpawner` -> `WorldInstanceSpawner` - `SceneFilter` -> `WorldFilter` - `SceneLoaderError` -> `WorldAssetLoaderError` - `SceneSpawnError` -> `WorldInstanceSpawnError` Note that I went with `bevy_world_serialization` over `bevy_ecs_serialization`, as that is what all of the internal features described themselves as. I think it is both more specific and does a better job of making itself decoupled from `bevy_ecs` proper. |
||
|
|
f6ee281a3b |
Make CPU visibility systems ignore meshes tagged with NoCpuCulling entirely. (#23107)
Currently, Bevy handles meshes tagged with `NoCpuCulling` by simply always adding them to `VisibleEntities`. This is, however, inefficient, because `VisibleEntities` has to be repopulated with the entity IDs of such meshes every frame and copied to the render world. When scaling to millions of entities, this becomes a significant bottleneck. This PR changes the visibility systems to ignore meshes with `NoCpuCulling` entirely. Instead of being added to `VisibleEntities`, the mesh extraction systems instead use standard ECS queries to iterate over meshes with `NoCpuCulling` directly, in addition to any entities in `VisibleEntities` that use CPU culling. For efficiency, `RenderVisibleMeshEntities` now tracks mesh instances that are subject to CPU culling and those that opted out of CPU culling in two separate data structures. Note that this required changing the signatures of `DirtySpecializations` methods to return a tuple of references instead of a reference to a tuple. Although that change looks complicated, it's actually just reshuffling to accommodate this slight type change, not a change in logic. On `bevy_city`, `check_visibility` takes a median of 1.19 ms, and `check_dir_light_mesh_visibility` takes a median of 4.33 ms. With this patch, these systems entirely disappear if `NoCpuCulling` is added to every mesh. <img width="2756" height="1800" alt="Screenshot 2026-02-22 020929" src="https://github.com/user-attachments/assets/18048399-bcfd-4165-8491-8d126d73534e" /> |
||
|
|
a9192fa63f |
refactor: remove threshold configuration from StaticTransformOptimiza… (#23193)
# Objective - Fixes #23192 by removing the threshold from the associated functions ## Solution - We are removing the threshold all together, so the optimization is either enabled or disabled now. ## Migration guide - Don't rely on from_threshold calls, either have the optimizations enabled or disabled. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
|
|
107cc2539f |
Batch meshes with morph targets. (#23023)
Right now, Bevy can't batch meshes with morph targets together, because the morph targets are packed into a morph texture, which is non-bindless. To fix this, this PR adds support for batching morph targets together on platforms with storage buffers. Morph displacements are allocated using the mesh allocator, just like vertex and index buffers are. This PR also improves the API for supplying morph targets to a mesh. Today, the application must create a `MorphTargetImage` explicitly to store the morph targets, which is cumbersome. This patch changes the `Mesh` API to instead take morph targets as a flat vector. Internally, if the platform doesn't support storage buffers, the morph targets are converted to a morph target image; if the platform does support storage buffers, however, the morph targets are packed in the mesh allocator. This patch is a prerequisite for skin caching, because skin caching also applies to morph targets, and skin caching wants to skin many meshes at a time. Using a morph target image would either require batch breaking logic or bindless, neither of which are desirable for a feature that be simple and work on WebGPU, so I opted to make morph targets batchable instead. On the `many_morph_targets` example, I went from 5.55 ms/frame to 2.80 ms/frame, a 1.98x speedup. --------- Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com> |
||
|
|
b8153cbce4 |
Add motion blur option to various stress tests (#23060)
## Objective Add a motion blur option to various stress tests. This is useful for benchmarking, but also for bug reproduction - not many examples use motion blur. In particular, I want this to repro a suspected bug with motion blur and morph targets. ## Solution Add an opt-in `--motion-blur` argument to `many_foxes`, `many_morph_targets` and `many_cubes`. I'm a bit unhappy about the cut and paste. Maybe there should be some shared utilities for the stress tests. ## Testing Tested on Native/Win10/Nvidia/Vulkan. ```sh cargo run --example many_foxes -- --motion-blur cargo run --example many_morph_targets -- --motion-blur cargo run --example many_many_cubes -- --motion-blur ``` |
||
|
|
b6816816cc |
Add the ability to set the number of cubes in many_cubes. (#23101)
Surprisingly, despite the number of settings in `many_cubes`, it was missing the most obvious one: setting the number of cubes. This commit adds this feature. It's a little more complicated than one might expect, because of the way the number of cubes is never actually set directly, as well as the fact that the demo supports three different patterns. |
||
|
|
f255b8e57a |
Upgrade glam, hexasphere, rand & uuid to latest versions (#22928)
# Objective - `glam`, `hexasphere` & `rand` have released their latest versions, update Bevy to support them. ## Solution - The above have been updated to their compatible versions. `rand_distr` updated as well to match `rand` v0.10 support. - `rand_chacha` is soft deprecated and no longer used by `rand`, so its usage has been changed to `chacha20` to match `rand` dep tree. - `uuid` is in the process of updating to `getrandom` v0.4, which `rand` v0.10 supports. This PR remains in draft until a new `uuid` release hits crates.io. - `RngCore` is now `Rng`, and `Rng` is now `RngExt`, so this required updating across many files. - `choose_multiple` method is deprecated, changed to `sample`. ## Testing - Chase all compiler errors, since this should not regress any already existing behaviour. - This must pass CI without regressions. ## Additional Notes `getrandom` v0.4 doesn't add anything new for Web WASM support, so the same `wasm_js` feature is used. |
||
|
|
0339de9571 |
Add many_morph_targets stress test (#18536)
## Objective I wanted to benchmark the morph target changes in #18465. I also wanted to test morph targets on multiple meshes, which is not covered by existing examples. ## Solution Add a stress test for morph targets, similar to `many_cubes` and `many_foxes`. Spawns a ton of meshes (defaults to 1024) and animates their morph target weights.  ## Testing ```sh cargo run --example many_morph_targets # Test different mesh counts. cargo run --example many_morph_targets -- --count 42 ``` Tested on Win10/Vulkan/Nvidia, Wasm/WebGL/Chrome/Win10/Nvidia. |
||
|
|
9fd2637846 |
Add tools to avoid unnecessary AssetEvent::Modified events that lead to rendering performance costs (#16751) (#22460)
# Objective - Fixes #16751 ## Solution - `Assets::get_mut` now returns a wrapper `AssetMut` type instead of `&mut impl Asset`. - `AssetMut` implements `Deref` and `DerefMut`. - `DerefMut` marks assets as changed. - when dropped `AssetMut` will add `AssetEvent::Modified` event to a queue only in case asset was marked as changed. ## Testing - Did you test these changes? If so, how? - No unit tests were added, change is pretty straightforward. - Test project: https://github.com/MatrixDev/bevy-feature-16751-test. - With change: ~100 fps. - Without change: ~15 fps. - Are there any parts that need more testing? - I don't really see how this can break anything or add a measurable overhead. - `AssetEvent::Modified` will now be sent after the asset was modified instead of before. It should not affect anything but still worth noting. - How can other people (reviewers) test your changes? Is there anything specific they need to know? - Have a big amount of entities that constantly update their materials. - Properties of those materials should be animated in a stepped maned (like changing color every 0.1 seconds). - Update material only if value has actually changed: ```rust if material.base_color != new_color { material.base_color = new_color; } ``` - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? - tested on macos (Mackbook M1) - not a platform-specific issue PS: This is my first PR, so please don’t judge. |
||
|
|
6ca4769128 |
Minimal responsive FontSize support (#22614)
# Objective
Add responsive font sizes supporting rem and viewport units to
`bevy_text` with minimal changes to the APIs and systems.
## Solution
Introduce a new `FontSize` enum:
```rust
pub enum FontSize {
/// Font Size in logical pixels.
Px(f32),
/// Font size as a percentage of the viewport width.
Vw(f32),
/// Font size as a percentage of the viewport height.
Vh(f32),
/// Font size as a percentage of the smaller of the viewport width and height.
VMin(f32),
/// Font size as a percentage of the larger of the viewport width and height.
VMax(f32),
/// Font Size relative to the value of the `RemSize` resource.
Rem(f32),
}
```
This replaces the `f32` value of `TextFont`'s `font_size` field.
The viewport variants work the same way as their respective `Val`
counterparts.
`Rem` values are multiplied by the value of the `RemSize` resource
(which newtypes an `f32`).
`FontSize` provides an `eval` method that takes a logical viewport size
and rem base size and returns an `f32` logical font size. The resolved
logical font size is then written into the `Attributes` passed to Cosmic
Text by `TextPipeline::update_buffer`.
Any text implementation using `bevy_text` must now provide viewport and
rem base values when calling `TextPipeline::update_buffer` or
`create_measure`.
`Text2d` uses the size of the primary window to resolve viewport values
(or `Vec2::splat(1000)` if no primary window is found). This is a
deliberate compromise, a single `Text2d` can be rendered to multiple
viewports using `RenderLayers`, so it's difficult to find a rule for
which viewport size should be chosen.
### Change detection
`ComputedTextBlock` has two new fields: `uses_viewport_sizes` and
`uses_rem_sizes`, which are set to true in `TextPipeline::update_buffer`
iff any text section in the block uses viewport or rem font sizes,
respectively.
The `ComputedTextBlock::needs_rerender` method has been modified to take
take two bool parameters:
```rust
pub fn needs_rerender(
&self,
is_viewport_size_changed: bool,
is_rem_size_changed: bool,
) -> bool {
self.needs_rerender
|| (is_viewport_size_changed && self.uses_viewport_sizes)
|| (is_rem_size_changed && self.uses_rem_sizes)
}
```
This ensures that text reupdates will also be scheduled if one of the text section's uses a viewport font size and the local viewport size changed, or if one of the text section's uses a rem font size and the rem size changed.
#### Limitations
There are some limitations because we don't have any sort of font style inheritance yet:
* "rem" units aren't proper rem units, and just based on the value of a resource.
* "em" units are resolved based on inherited font size, so can't be implemented without inheritance support.
#### Notes
* This PR is quite small and not very technical. Reviewers don't need to be especially familiar with `bevy_text`. Most of the changes are to the examples.
* We could consider using `Val` instead of `FontSize`, then we could use `Val`'s constructor functions which would be much nicer, but some variants might not have sensible interpretations in both UI and Text2d contexts. Also we'd have to make `Val` accessible to `bevy_text`.
## Testing
The changes to the text systems are relatively trivial and easy to understand. I already added a minor change to the `text` example to use `Vh` font size for the "hello bevy" text in the bottom right corner. If you change the size of the window, you should see the text change size in response. The text initially flickers before it updates because of some unrelated asset/image changes that mean that font textures aren't ready until the frame after the text update that changes the font size.
Most of the example migrations were automated using regular expressions, and there are bound to be mistakes in those changes. It's infeasible to check every single example thoroughly, but it's early enough in the release cycle that I don't think we should be too worried if a few bugs slip in.
---------
Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
|
||
|
|
f7be67e820 |
WinitSettings cleanup (#22650)
# Objective #### Some cleanup 1. `many_gradients` inserts `WinitSettings` twice. 2. Replace manual configuration where `WinitSettings::continuous()` could be used. |
||
|
|
842032d1f7 |
Assign indices to light probes in the clustered objects list, and refactor the clustering code. (#22621)
At the moment, clustering is a three-step process: 1. `assign_objects_to_clusters` runs on all clusterable objects during the `PostUpdate` schedule, creating lists of all clusterable objects in each view. 2. During the extraction phase, `extract_clusters` runs on all views that had clusters created for them, linearizing the clusters into a list of `ExtractedClusterableObjectElement::ClusterHeader` commands followed by other `ExtractedClusterableObjectElement`s, one for each object in the cluster. Each `ExtractedClusterableObjectElement` specifies the render world entity for the clusterable object. 3. In the render world, `prepare_clusters` processes all `ExtractedClusterableObjectElement` commands to create the GPU buffers, looking up each clustered object in the `GlobalClusterableObjectMeta` table in order to translate from entity to index. Unfortunately, there are two main problems with this: a. Light probes don't have render world entities at all and are instead tracked in `RenderViewLightProbes` components in the render world. Thus step (2) silently fails for them. b. The `GlobalClusterableObjectMeta` table only contains clustered lights and decals, so even if light probes had render-world entities, step (3) would still fail. The end result is that the GPU ends up consulting bogus out-of-bounds indices that may or may not actually refer to the light probe when traversing clusters. This PR fixes the issues: * I extended `extract_clusters` to support light probes, by adding `ExtractedClusterableObjectElement::ReflectionProbe` and `ExtractedClusterableObjectElement::IrradianceVolume` variants. These variants reference the *main* world entities for light probes, since no render-world entities exist for them. * When processing the new `ExtractedClusterableObjectElement` commands, `prepare_clusters` uses the `RenderViewLightProbes` to find the index in the reflection probe or irradiance volume table as appropriate and supply it to the GPU. Note that this step might fail if a texture that the light probe needs hasn't been loaded yet. In this case, an index of -1 is stored, and the shader skips it. This isn't the optimum behavior; ideally we wouldn't cluster such objects at all. However, it was a minimally-invasive change. * I renamed types that referenced clusterable objects to refer to clusterable *lights* specifically if the types only dealt with lights, to reduce confusion in the future. * The `VisibleClusterableObjects` type is currently overloaded to both serve as a component, in which case it contains *all* clusterable objects associated with a view, and to serve as a container for the objects associated with a cluster. Not only is this confusing, but it's also wasteful, as there's bookkeeping that the type does that's not needed when it's serving as a component. I split the type into the component `VisibleClusterableObjects` and the helper structure `ObjectsInCluster`, and encapsulated logic within each type in order to make `assign_objects_to_clusters` easier to understand. * The `gather_light_probes` system performed its own frustum culling on light probes separately from the frustum culling that `assign_objects_to_clusters` also does. This was wasteful and confusing, especially since the frustum culling algorithms differed between the two systems, so I simplified the logic so that `assign_objects_to_clusters` fills out a table for `gather_light_probes` to use. * `compute_radiances` in `environment_map.wgsl` was broken, as it neglected to set `light_from_world` to the identity matrix when falling back to the view environment map when a reflection probe wasn't found. This would cause specular to vanish in some cases (e.g. in the `reflection_probes` example). I fixed the problem. This commit is a prerequisite for #22610, as multiple light probes are too broken without it. |
||
|
|
78febd262d |
FontAtlasSet total font data size helper function (#22598)
# Objective Add a function that returns the sum of the sizes of all the image data for all the fonts. Needed for an LRU buffer implementation that would remove the text atlases for the least recently used font face when the total font image data exceeds a set limit. ## Solution Add a `total_bytes` method on `FontAtlasSet` that returns the sum of the lengths of the image data for all fonts. Implementation might be too naive, I'm not sure, but it seems sufficient to me. ## Testing I changed the `many_text2d` example's `info!` output to include the total font image bytes. |
||
|
|
777098ebc7 |
SpriteMesh + SpriteMaterial (#22484)
# Objective The objective is reworking `Sprites` to use the `Mesh` backend instead of the current, severely outdated, one. The `2D Ecosystem` would grately benefit from sprites having access to all the mesh utilities, such as `ExtensionMaterials`, without having to be updated and maintained separately. We could allow things such as attaching a shader to a Sprite to give it an outline, which is a very accessible feature in other engines that Bevy currently lacks any streamlined support for (#16170). This PR notably contributes to #13265, while also completely fixing #15021 and possibly other sprite-related issues. ## Solution I've introduced a new `SpriteMesh` component. It is an exact replica of `Sprite`, having the same API, except for an extra `alpha_mode` field. The purpose of this is to eventually remove `Sprite` and all its backend, and rename `SpriteMesh` to `Sprite`, which should hopefully be a seamless transition for our end-users. Internally, `SpriteMesh` is just an abstraction over adding a `Mesh2d` and the new `SpriteMaterial` to an entity, and a user should be able to just do the latter if they prefer, essentially merging the ease-of-use of `Sprites` and flexibility of `Meshes`. In its current state, `SpriteMesh` is completely usable and produces the same behavior as `Sprite`. There is however a notable exception: - The `max_corner_scale` parameter when slicing now produces different results. Before, in the [`sprite_slice`](https://bevy.org/examples/2d-rendering/sprite-slice/) example, it was set to `0.2`, causing the corners to be half of their scale. I am fairly confident this was a bug since I haven't found any correlation between `0.2` and what was being displayed. Instead, the `SpriteMesh` now requires `0.5` to produce the same result, `0.2` resulting in a much smaller corner (a fifth of its size). | Sprite, `max_corner_scale=0.2` | SpriteMesh, `max_corner_scale=0.2` | SpriteMesh, `max_corner_scale=0.5` | | ------------- | ------------- | ------------- | | <img width="347" height="262" alt="image" src="https://github.com/user-attachments/assets/76cc6ffe-155b-4ba5-88ff-494248948efa" /> | <img width="337" height="256" alt="image" src="https://github.com/user-attachments/assets/4a28739a-b058-4286-908c-8d6e83295b86" /> | <img width="329" height="248" alt="image" src="https://github.com/user-attachments/assets/7c758e33-1379-4cff-8f53-13f678283264" />| Otherwise, replacing a `Sprite` with a `SpriteMesh` should have no visual difference. There were also a few bugs that I encountered with the `Sprites`, related to scaling and flipping them a certain way producing weird behavior such as the sprite completely disappearing. These are not present in the new `SpriteMesh`. While the API and visual behavior remain the same, the underlying logic has been completely rewritten: - No custom `RenderApp` logic is used anymore, `SpriteMesh`/`SpriteMaterial` exclusively uses the API provided by `Material2d`. - `Slicing` and `Tiling` are now done purely inside the shader through UV mapping and manipulation (all done in constant time). Before, they generated multiple `SpriteInstances` for each tile / slice, being extremely unmaintainable and adding significant overhead. This fixes #15021. ## Testing - I mostly ran tests by overlapping `Sprites` with `SpriteMeshes` having the exact same configuration and making sure there are no differences in behavior. However, I did essentially have to rework the whole `Sprite` API to work with `Meshes` and I can't guarantee that I haven't missed anything. - I tested the new `SpriteMeshes` in [`bevymark`](https://bevy.org/examples/stress-tests/bevymark/). I'd say there's an ~2-2.5x increase in performance over `Sprites` when alpha masking them, and a ~0.5x decrease when alpha blending. However, alpha masking is definitely the more common choice for Sprites, and the alpha blending performance should be improved significantly in the near future as part of #13265. ## Problems These are a few notable problems that should ideally be resolved before fully replacing `Sprite` with the new `SpriteMesh`: - The `TextureAtlasLayout` is an asset that the `SpriteMaterial` depends on to generate its `BindGroup`. It is currently just read when the `SpriteMesh` is added (or changed) and baked into the `SpriteMaterial`, which means there's no hot reloading for it (changing a `TextureAtlasLayout` won't update the material). I believe this is best fixed by reading the `TextureAtlasLayout` asset inside `as_bind_group_shader_type()`, same as the `Sprite's` `Image`. - Creating a new `SpriteMaterial` for each new `SpriteMesh` and its changes is really bad for performance, which is why I've added a rudimentary material caching solution through a `HashMap<SpriteMesh, Handle<SpriteMaterial>`. However, I do believe there's significant potential for further optimizations in this area. - Currently, a significant chunk of `SpriteMaterialUniform` (about `40 bytes`) is taken by data used for slicing. This data is useless for `Sprites` which don't use slicing, and so it adds overhead. Ideally, this data would be placed in a separate binding that is only written and read on the GPU if a certain `ShaderDef` is set. --------- Co-authored-by: IceSentry <IceSentry@users.noreply.github.com> |
||
|
|
a88af65738 |
Contact Shadows (#22382)
# Objective - Implement contact shadows to add fine shadow detail where shadow cascades cannot. ## Solution - Extend our existing pbr implementation using our existing raymarching functions. --- ## Showcase <img width="1824" height="1180" alt="image" src="https://github.com/user-attachments/assets/e93b79c5-c596-4a9e-b94d-20bdde1d863b" /> <img width="1824" height="1180" alt="image" src="https://github.com/user-attachments/assets/0fd7dffa-60b8-4b92-8fad-7f993d4d89dd" /> https://github.com/user-attachments/assets/e74b190d-9ae3-4aaf-97f0-b520930a0667 https://github.com/user-attachments/assets/e80ccb26-bbaa-4d25-a823-8ea12354c5b9 https://github.com/user-attachments/assets/b04f4b00-92bd-4a2f-b7dd-5157d8fbe0ab <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/b7629908-dd32-48db-8ee7-a4d2dd8f66c2" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/3de0258e-9191-4180-ac57-41b32e1205bd" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/951477f9-e9a9-426f-ae8d-18ae50cc7b85" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/2291453c-da57-4fcc-a6b0-f60f6eac6cbb" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/5820cdff-ea54-4294-b520-2a8d8dc24996" /> <img width="1073" height="685" alt="image" src="https://github.com/user-attachments/assets/3ea16481-7689-4e99-87e2-1589f1532e4c" /> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: charlotte 🌸 <charlotte.c.mcelwain@gmail.com> |
||
|
|
e5d4dfd7a6 |
Minimal Font Families, Font Queries, Collections, System Fonts, Stretch, and Slant support (#22156)
# Objective
Implement support for the remaining missing text features with minimal
changes.
## Solution
`TextFont` has been expanded to include new fields:
```rust
pub struct TextFont {
pub font: FontSource,
pub font_size: f32,
pub weight: FontWeight,
pub width: FontWidth,
pub style: FontStyle,
pub font_smoothing: FontSmoothing,
pub font_features: FontFeatures,
}
```
FontSource has two variants: Handle, which identifies a font by asset
handle, and Family, which selects a font by its family name.
`FontWidth` is a newtype struct representing OpenType font stretch
classifications ranging from ULTRA_CONDENSED (50%) to ULTRA_EXPANDED
(200%).
`FontStyle` is an enum used to set the slant style of a font, either
`Normal`, `Italic`, or `Oblique`.
The system font support is very barebones. You load them using the
`CosmicFontSystem` resource:
```rust
font_system.db_mut().load_system_fonts()
```
Then they are available to be selected by family name using
`FontSource::Family`.
### Other changes
* `TextPipelines`'s `glyph_info` field has been removed. There is no
need to collect the section infos or perform any querys during text
layout updates, so that code has been removed as well.
* `update_text_layout_info` used some `try_for_each` with some nested
closures which was unnecessarily complicated again. They've been
replaced with a regular for loop.
* After font assets are loaded there's a new system
`load_font_assets_into_fontdb_system` that automatically adds them to
cosmic text's font database. Then they are available to be looked up by
family name as well as by asset handle.
* There aren't are performance motivated changes but layout updates seem
to be overall significantly more efficient now, with a slight regression
for very large numbers of short, single section text entities.
* Font texture atlases are no longer automatically cleared when the font
asset they were generated from is removed. There is no way to remove
individual fonts from cosmic text's `FontSystem`, so the font is still
accessible using the family name with `FontSource::family` and removing
the text atlases naively could cause a panic since rendering expects
them to be present.
## Testing
```
cargo run --example font_query
```
---
## Showcase
<img width="1229" height="591" alt="font-query"
src="https://github.com/user-attachments/assets/23f5aaa2-fdb8-4448-9b4e-9d65d6431107"
/>
---------
Co-authored-by: Thierry Berger <contact@thierryberger.com>
|
||
|
|
a912a48401 |
Bevymark 3D (#22298)
# Objective - Add a stress test that exercises the 3d mesh pipeline for dynamic scenes. - Large static scenes like caldera hotel don't expose performance issues when many meshes are moving. - Give us a way to benchmark PRs like - https://github.com/bevyengine/bevy/pull/22297 - https://github.com/bevyengine/bevy/pull/22281 - https://github.com/bevyengine/bevy/pull/22226 ## Solution - Make a 3d version of `bevymark`, sticking to the existing patterns as closely as possible. ## Testing <img width="1072" height="684" alt="image" src="https://github.com/user-attachments/assets/41214ba9-ffad-471d-a320-1f007490dead" /> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
|
|
b0acc82097 |
Fix many_foxes animation controls (#22292)
# Objective - Fix #22285 ## Solution - Example is relying on component `AnimationTransitions` which wasn't added. Add it |
||
|
|
83e57e0175 |
Optimize transform propagation for dynamic scenes (#22281)
# Objective - Follow up from previous transform optimization (#18589), make the `mark_dirty_trees` system more intelligent - don't run this expensive static scene optimization for dynamic scenes. - Using a threshold was mentioned as a follow up in that PR, and we also want this threshold to be user-configurable. - This was not implemented previously because the optimizations were still large improvements even in dynamic scenes thanks to the improved parallelism #17840 ## Solution - Don't run static scene optimization (dirty tree tracking) for very dynamic scenes - defined here as scenes where more than 30% of objects have their `Transform` updated. - This is configurable with a percentage threshold, or it can be unconditionally enabled or disabled when setting to `0.0` or `1.0` to avoid the cost of computing the threshold. - For dynamic scenes, this makes transform prop much faster, twice as fast in the stress tests shown here. ## Testing transform_hierarchy stress tests, all of these cases spawn about a quarter million entities: - humanoids_active - dynamic scene that should be faster than `main`: <img width="609" height="395" alt="image" src="https://github.com/user-attachments/assets/bf3d6b93-aa09-4440-b8ac-18af7e46a00f" /> - humanoids_inactive - static scene that should be unchanged from `main`: <img width="631" height="377" alt="image" src="https://github.com/user-attachments/assets/a0306109-600b-4cdd-a217-5cc15e269bca" /> - humanoids_mixed - half dynamic scene that should be faster than `main` <img width="604" height="372" alt="image" src="https://github.com/user-attachments/assets/2751ece2-d4b9-4daa-af24-fe379eaf75b2" /> - large_tree - dynamic scene (50% of entities are moved) we expect to see improvements <img width="665" height="371" alt="image" src="https://github.com/user-attachments/assets/c6b08abe-eb1d-44fb-be36-457f9d5ba78e" /> |
||
|
|
5436f6a952 |
Name the fields of FontAtlasKey (#22161)
# Objective Name the fields of the `FontAtlasKey` struct. ## Solution Named the fields and added doc comments. |
||
|
|
7d84e5bed1 |
Update rotate_cubes system to use par_iter_mut() to reduce its impact on performance (#21755)
Quick follow-up to #21745. Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
ea953aace0 |
Add a dense layout to the many_cubes stress test (#21745)
# Objective - Test the impact that many overlapping cubes, all within the camera frustum, have on visibility/occlusion calculations, especially with continuous rotation and shadows enabled. ## Solution - Add a dense cube layout using a static camera, and a toggle for individual cube rotation. ## Testing - I ran the test in several stages, each causing a considerable performance drop. - Stage 1: using just the dense layout. - Stage 2: using the dense layout with rotation or shadows enabled. - Stage 3: using the dense layout with both rotation and shadows enabled. ``` cargo run --release --example many_cubes -- --layout dense --rotate-cubes --shadows ``` --- ## Showcase <img width="1904" height="940" alt="image" src="https://github.com/user-attachments/assets/6fc811aa-81fa-4130-a6e0-0a017bc99f19" /> |
||
|
|
279a8678d4 |
Improved Entity Lifecycle: remove flushing, support manual spawning and despawning (#19451)
# Objective This is the next step for #19430 and is also convinient for #18670. For context, the way entities work on main is as a "allocate and use" system. Entity ids are allocated, and given a location. The location can then be changed, etc. Entities that are free have an invalid location. To allocate an entity, one must also set its location. This introduced the need for pending entities, where an entity would be reserved, pending, and at some point flushed. Pending and free entities have an invalid location, and others are assumed to have a valid one. This paradigm has a number of downsides: First, the entities metadata table is inseparable from the allocator, which makes remote reservation challenging. Second, the `World` must be flushed, even to do simple things, like allocate a temporary entity id. Third, users have little control over entity ids, only interacting with conceptual entities. This made things like `Entities::alloc_at` clunky and slow, leading to its removal, despite some users still having valid need of it. So the goal of this PR is to: - Decouple `Entities` from entity allocation to make room for other allocators and resolve `alloc_at` issues. - Decouple entity allocation from spawning to make reservation a moot point. - Introduce constructing and destructing entities, in addition to spawn/despawn. - Change `reserve` and `flush` patterns to `alloc` and `construct` patterns. It is possible to break this up into multiple prs, as I originally intended, but doing so would require lots of temporary scaffolding that would both hurt performance and make things harder to review. ## Solution This solution builds on #19433, which changed the representation of invalid entity locations from a constant to `None`. There's quite a few steps to this, each somewhat controversial: ### Entities with no location This pr introduces the idea of entity rows both with and without locations. This corresponds to entities that are constructed (the row has a location) and not constructed (the row has no location). When a row is free or pending, it is not constructed. When a row is outside the range of the meta list, it still exists; it's just not constructed. This extends to conceptual entities; conceptual entities may now be in one of 3 states: empty (constructed; no components), normal (constructed; 1 or more components), or null (not constructed). This extends to entity pointers (`EntityWorldMut`, etc): These now can point to "null"/not constructed entities. Depending on the privilege of the pointer, these can also construct or destruct the entity. This also changes how `Entity` ids relate to conceptual entities. An `Entity` now exists if its generation matches that of its row. An `Entity` that has the right generation for its row will claim to exist, even if it is not constructed. This means, for example, an `Entity` manually constructed with a large index and generation of 0 *will* exist if it has not been allocated yet. ### `Entities` is separate from the allocator This pr separates entity allocation from `Entities`. `Entities` is now only focused on tracking entity metadata, etc. The new `EntitiesAllocator` on `World` manages all allocations. This forces `Entities` to not rely on allocator state to determine if entities exist, etc, which is convinient for remote reservation and needed for custom allocators. It also paves the way for allocators not housed within the `World`, makes some unsafe code easier since the allocator and metadata live under different pointers, etc. This separation requires thinking about interactions with `Entities` in a new way. Previously, the `Entities` set the rules for what entities are valid and what entities are not. Now, it has no way of knowing. Instead, interaction with `Entities` are more like declaring some information for it to track than changing some information it was already tracking. To reflect this, `set` has been split up into `declare` and `update`. ### Constructing and destructing As mentioned, entities that have no location (not constructed) can be constructed at any time. This takes on exactly the same meaning as the previous `spawn_non_existent`. It creates/declares a location instead of updating an old one. As an example, this makes spawning an entity now literately just allocate a new id and construct it immediately. Conversely, entities that are constructed may be destructed. This removes all components and despawns related entities, just like `despawn`. The only difference is that destructing does not free the entity id for reuse. Between constructing and destructing, all needs for `alloc_at` are resolved. If you want to keep the id for custom reuse, just destruct instead of despawn! Despawn, now just destructs the entity and frees it. Destructing a not constructed entity will do nothing. Constructing an already constructed entity will panic. This is to guard against users constructing a manually formed `Entity` that the allocator could later hand out. However, public construction methods have proper error handling for this. Despawning a not constructed entity just frees its id. ### No more flushing All places that once needed to reserve and flush entity ids now allocate and construct them instead. This improves performance and simplifies things. ### Flow chart  (Thanks @ItsDoot) ## Testing - CI - Some new tests - A few deleted (no longer applicable) tests - If you see something you think should have a test case, I'll gladly add it. ## Showcase Here's an example of constructing and destructing ```rust let e4 = world.spawn_null(); world .entity_mut(e4) .construct((TableStored("junk"), A(0))) .unwrap() .destruct() .construct((TableStored("def"), A(456))) .unwrap(); ``` ## Future Work - [x] More expansive docs. This should definitely should be done, but I'd rather do that in a future pr to separate writing review from code review. If you have more ideas for how to introduce users to these concepts, I'd like to see them. As it is, we don't do a very good job of explaining entities to users. Ex: `Entity` doesn't always correspond to a conceptual entity. - [ ] Try to remove panics from `EntityWorldMut`. There is (and was) a lot of assuming the entity is constructed there (was assuming it was not despawned). - [ ] A lot of names are still centered around spawn/despawn, which is more user-friendly than construct/destruct but less precise. Might be worth changing these over. - [ ] Making a centralized bundle despawner would make sense now. - [ ] Of course, build on this for remote reservation and, potentially, for paged entities. ## Performance <details> <summary>Benchmarks</summary> ```txt critcmp main pr19451 -t 1 group main pr19451 ----- ---- ------- add_remove/sparse_set 1.13 594.7±6.80µs ? ?/sec 1.00 527.4±8.01µs ? ?/sec add_remove/table 1.08 799.6±15.53µs ? ?/sec 1.00 739.7±15.10µs ? ?/sec add_remove_big/sparse_set 1.10 614.6±6.50µs ? ?/sec 1.00 557.0±19.04µs ? ?/sec add_remove_big/table 1.03 2.8±0.01ms ? ?/sec 1.00 2.7±0.02ms ? ?/sec added_archetypes/archetype_count/100 1.01 30.9±0.50µs ? ?/sec 1.00 30.5±0.44µs ? ?/sec added_archetypes/archetype_count/1000 1.00 638.0±19.77µs ? ?/sec 1.03 657.0±73.61µs ? ?/sec added_archetypes/archetype_count/10000 1.02 5.5±0.14ms ? ?/sec 1.00 5.4±0.09ms ? ?/sec all_added_detection/50000_entities_ecs::change_detection::Sparse 1.02 47.9±1.22µs ? ?/sec 1.00 46.8±0.40µs ? ?/sec all_added_detection/50000_entities_ecs::change_detection::Table 1.02 45.4±1.89µs ? ?/sec 1.00 44.6±0.78µs ? ?/sec build_schedule/1000_schedule 1.02 942.6±11.53ms ? ?/sec 1.00 925.2±10.35ms ? ?/sec build_schedule/100_schedule 1.01 5.8±0.12ms ? ?/sec 1.00 5.7±0.12ms ? ?/sec build_schedule/100_schedule_no_constraints 1.03 803.1±28.93µs ? ?/sec 1.00 781.1±50.11µs ? ?/sec build_schedule/500_schedule_no_constraints 1.00 5.6±0.31ms ? ?/sec 1.08 6.0±0.27ms ? ?/sec busy_systems/01x_entities_03_systems 1.00 24.4±1.35µs ? ?/sec 1.01 24.7±1.35µs ? ?/sec busy_systems/03x_entities_03_systems 1.00 38.1±1.70µs ? ?/sec 1.04 39.7±1.49µs ? ?/sec busy_systems/03x_entities_09_systems 1.01 111.4±2.27µs ? ?/sec 1.00 109.9±2.46µs ? ?/sec busy_systems/03x_entities_15_systems 1.00 174.8±2.56µs ? ?/sec 1.01 176.6±4.22µs ? ?/sec contrived/03x_entities_09_systems 1.00 59.0±2.92µs ? ?/sec 1.01 59.8±3.03µs ? ?/sec contrived/03x_entities_15_systems 1.00 97.5±4.87µs ? ?/sec 1.01 98.8±4.69µs ? ?/sec contrived/05x_entities_09_systems 1.00 75.3±3.76µs ? ?/sec 1.01 76.4±4.11µs ? ?/sec despawn_world/10000_entities 1.32 344.8±4.47µs ? ?/sec 1.00 261.4±4.91µs ? ?/sec despawn_world/100_entities 1.22 4.3±0.04µs ? ?/sec 1.00 3.5±0.54µs ? ?/sec despawn_world/1_entities 1.01 169.6±7.88ns ? ?/sec 1.00 167.8±11.45ns ? ?/sec despawn_world_recursive/10000_entities 1.20 1723.0±53.82µs ? ?/sec 1.00 1437.0±26.11µs ? ?/sec despawn_world_recursive/100_entities 1.16 17.9±0.10µs ? ?/sec 1.00 15.5±0.16µs ? ?/sec despawn_world_recursive/1_entities 1.01 372.8±15.68ns ? ?/sec 1.00 367.7±16.90ns ? ?/sec ecs::entity_cloning::hierarchy_many/clone 1.03 227.9±24.67µs 1559.9 KElem/sec 1.00 221.1±29.74µs 1607.8 KElem/sec ecs::entity_cloning::hierarchy_many/reflect 1.00 406.2±23.46µs 875.2 KElem/sec 1.02 413.9±22.45µs 858.9 KElem/sec ecs::entity_cloning::hierarchy_tall/clone 1.01 12.2±0.34µs 4.0 MElem/sec 1.00 12.0±1.41µs 4.1 MElem/sec ecs::entity_cloning::hierarchy_tall/reflect 1.02 15.3±0.39µs 3.2 MElem/sec 1.00 15.0±2.14µs 3.2 MElem/sec ecs::entity_cloning::single/clone 1.02 659.0±100.01ns 1481.8 KElem/sec 1.00 643.3±101.49ns 1517.9 KElem/sec ecs::entity_cloning::single/reflect 1.03 1135.2±72.17ns 860.2 KElem/sec 1.00 1098.3±65.99ns 889.1 KElem/sec empty_archetypes/for_each/10 1.02 8.1±0.57µs ? ?/sec 1.00 8.0±0.37µs ? ?/sec empty_archetypes/for_each/100 1.01 8.1±0.34µs ? ?/sec 1.00 8.1±0.28µs ? ?/sec empty_archetypes/for_each/1000 1.03 8.4±0.25µs ? ?/sec 1.00 8.2±0.29µs ? ?/sec empty_archetypes/iter/100 1.01 8.1±0.29µs ? ?/sec 1.00 8.0±0.34µs ? ?/sec empty_archetypes/iter/1000 1.02 8.5±0.31µs ? ?/sec 1.00 8.4±0.62µs ? ?/sec empty_archetypes/iter/10000 1.01 10.6±1.22µs ? ?/sec 1.00 10.5±0.49µs ? ?/sec empty_archetypes/par_for_each/10 1.01 8.8±0.49µs ? ?/sec 1.00 8.7±0.31µs ? ?/sec empty_archetypes/par_for_each/100 1.00 8.7±0.48µs ? ?/sec 1.04 9.0±0.34µs ? ?/sec empty_archetypes/par_for_each/10000 1.01 21.2±0.41µs ? ?/sec 1.00 20.9±0.44µs ? ?/sec empty_commands/0_entities 1.72 3.7±0.01ns ? ?/sec 1.00 2.1±0.02ns ? ?/sec empty_systems/100_systems 1.00 82.9±3.29µs ? ?/sec 1.07 88.3±3.77µs ? ?/sec empty_systems/2_systems 1.01 8.2±0.71µs ? ?/sec 1.00 8.2±0.38µs ? ?/sec empty_systems/4_systems 1.00 8.2±0.72µs ? ?/sec 1.03 8.4±0.71µs ? ?/sec entity_hash/entity_set_build/10000 1.10 45.9±1.60µs 207.7 MElem/sec 1.00 41.6±0.39µs 229.0 MElem/sec entity_hash/entity_set_build/3162 1.06 12.7±0.77µs 236.7 MElem/sec 1.00 12.0±0.75µs 250.6 MElem/sec entity_hash/entity_set_lookup_hit/10000 1.02 14.5±0.30µs 658.3 MElem/sec 1.00 14.2±0.07µs 672.6 MElem/sec entity_hash/entity_set_lookup_hit/3162 1.01 4.4±0.03µs 682.7 MElem/sec 1.00 4.4±0.01µs 691.3 MElem/sec entity_hash/entity_set_lookup_miss_gen/10000 1.01 61.3±4.12µs 155.6 MElem/sec 1.00 60.6±1.47µs 157.3 MElem/sec entity_hash/entity_set_lookup_miss_gen/3162 1.00 9.5±0.02µs 316.3 MElem/sec 1.01 9.7±0.88µs 311.7 MElem/sec entity_hash/entity_set_lookup_miss_id/100 1.00 145.5±1.49ns 655.4 MElem/sec 1.03 149.8±1.59ns 636.7 MElem/sec entity_hash/entity_set_lookup_miss_id/10000 1.85 63.9±3.57µs 149.3 MElem/sec 1.00 34.6±3.81µs 275.8 MElem/sec entity_hash/entity_set_lookup_miss_id/316 1.00 562.0±9.58ns 536.2 MElem/sec 1.02 573.9±1.27ns 525.1 MElem/sec entity_hash/entity_set_lookup_miss_id/3162 1.03 9.1±0.10µs 330.7 MElem/sec 1.00 8.9±0.24µs 339.0 MElem/sec event_propagation/four_event_types 1.12 541.5±3.84µs ? ?/sec 1.00 482.7±4.64µs ? ?/sec event_propagation/single_event_type 1.07 769.5±10.21µs ? ?/sec 1.00 715.9±15.16µs ? ?/sec event_propagation/single_event_type_no_listeners 1.56 393.4±2.89µs ? ?/sec 1.00 251.4±3.68µs ? ?/sec events_iter/size_16_events_100 1.01 64.0±0.18ns ? ?/sec 1.00 63.4±0.23ns ? ?/sec events_iter/size_4_events_100 1.02 64.8±0.90ns ? ?/sec 1.00 63.4±0.24ns ? ?/sec events_iter/size_4_events_1000 1.01 586.5±8.00ns ? ?/sec 1.00 579.1±4.93ns ? ?/sec events_send/size_16_events_100 1.00 142.7±24.34ns ? ?/sec 1.03 147.1±28.36ns ? ?/sec events_send/size_16_events_10000 1.01 12.2±0.13µs ? ?/sec 1.00 12.1±0.12µs ? ?/sec fake_commands/10000_commands 1.43 63.3±8.21µs ? ?/sec 1.00 44.1±0.16µs ? ?/sec fake_commands/1000_commands 1.40 6.2±0.01µs ? ?/sec 1.00 4.4±0.02µs ? ?/sec fake_commands/100_commands 1.38 629.4±1.69ns ? ?/sec 1.00 457.1±0.84ns ? ?/sec few_changed_detection/50000_entities_ecs::change_detection::Table 1.00 57.7±0.86µs ? ?/sec 1.07 61.6±1.19µs ? ?/sec few_changed_detection/5000_entities_ecs::change_detection::Sparse 1.05 5.4±0.53µs ? ?/sec 1.00 5.1±0.56µs ? ?/sec few_changed_detection/5000_entities_ecs::change_detection::Table 1.00 4.3±0.30µs ? ?/sec 1.18 5.1±0.35µs ? ?/sec insert_commands/insert 1.11 402.5±10.75µs ? ?/sec 1.00 363.6±8.07µs ? ?/sec insert_commands/insert_batch 1.00 174.9±3.03µs ? ?/sec 1.02 177.9±5.74µs ? ?/sec insert_simple/base 1.04 564.1±23.01µs ? ?/sec 1.00 544.3±60.70µs ? ?/sec insert_simple/unbatched 1.32 929.3±180.10µs ? ?/sec 1.00 704.1±132.88µs ? ?/sec iter_fragmented/base 1.02 280.0±2.86ns ? ?/sec 1.00 274.0±4.85ns ? ?/sec iter_fragmented/foreach 1.00 97.3±0.42ns ? ?/sec 1.03 100.6±3.44ns ? ?/sec iter_fragmented/foreach_wide 1.04 2.7±0.04µs ? ?/sec 1.00 2.6±0.03µs ? ?/sec iter_fragmented_sparse/base 1.00 5.6±0.05ns ? ?/sec 1.04 5.8±0.06ns ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_10000_entities_ecs::change_detection::Sparse 1.00 737.7±27.38µs ? ?/sec 1.01 747.5±30.01µs ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_10000_entities_ecs::change_detection::Table 1.02 678.3±25.13µs ? ?/sec 1.00 662.1±19.63µs ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_1000_entities_ecs::change_detection::Sparse 1.09 76.0±9.35µs ? ?/sec 1.00 70.0±3.29µs ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_1000_entities_ecs::change_detection::Table 1.03 64.7±3.40µs ? ?/sec 1.00 62.8±1.80µs ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_100_entities_ecs::change_detection::Table 1.02 7.6±0.12µs ? ?/sec 1.00 7.5±0.16µs ? ?/sec multiple_archetypes_none_changed_detection/100_archetypes_10_entities_ecs::change_detection::Sparse 1.00 1003.5±12.38ns ? ?/sec 1.01 1013.7±32.64ns ? ?/sec multiple_archetypes_none_changed_detection/20_archetypes_10_entities_ecs::change_detection::Sparse 1.03 187.1±21.18ns ? ?/sec 1.00 181.9±22.86ns ? ?/sec multiple_archetypes_none_changed_detection/5_archetypes_10_entities_ecs::change_detection::Sparse 1.00 52.8±8.19ns ? ?/sec 1.03 54.3±8.06ns ? ?/sec multiple_archetypes_none_changed_detection/5_archetypes_10_entities_ecs::change_detection::Table 1.00 46.8±2.23ns ? ?/sec 1.03 48.0±2.48ns ? ?/sec no_archetypes/system_count/0 1.00 16.3±0.17ns ? ?/sec 1.02 16.6±0.16ns ? ?/sec no_archetypes/system_count/100 1.02 851.5±9.32ns ? ?/sec 1.00 832.9±7.93ns ? ?/sec none_changed_detection/5000_entities_ecs::change_detection::Sparse 1.00 3.4±0.04µs ? ?/sec 1.02 3.5±0.05µs ? ?/sec nonempty_spawn_commands/10000_entities 1.89 261.1±6.99µs ? ?/sec 1.00 137.8±8.47µs ? ?/sec nonempty_spawn_commands/1000_entities 1.90 26.4±3.18µs ? ?/sec 1.00 13.9±2.38µs ? ?/sec nonempty_spawn_commands/100_entities 1.87 2.6±0.07µs ? ?/sec 1.00 1388.8±97.31ns ? ?/sec observe/trigger_simple 1.09 347.5±1.51µs ? ?/sec 1.00 317.7±2.62µs ? ?/sec observe/trigger_targets_simple/10000_entity 1.04 696.5±15.50µs ? ?/sec 1.00 672.0±13.88µs ? ?/sec par_iter_simple/with_0_fragment 1.01 34.4±0.51µs ? ?/sec 1.00 33.9±0.53µs ? ?/sec par_iter_simple/with_1000_fragment 1.04 45.5±0.93µs ? ?/sec 1.00 43.9±1.85µs ? ?/sec par_iter_simple/with_100_fragment 1.03 36.2±0.50µs ? ?/sec 1.00 35.1±0.44µs ? ?/sec par_iter_simple/with_10_fragment 1.03 37.5±0.97µs ? ?/sec 1.00 36.5±0.74µs ? ?/sec param/combinator_system/8_dyn_params_system 1.00 10.4±0.73µs ? ?/sec 1.01 10.5±0.79µs ? ?/sec param/combinator_system/8_piped_systems 1.05 8.0±0.65µs ? ?/sec 1.00 7.6±0.57µs ? ?/sec query_get/50000_entities_sparse 1.06 136.7±0.35µs ? ?/sec 1.00 128.6±0.44µs ? ?/sec query_get_many_10/50000_calls_sparse 1.02 1649.4±77.80µs ? ?/sec 1.00 1614.4±78.91µs ? ?/sec query_get_many_2/50000_calls_sparse 1.00 191.3±3.66µs ? ?/sec 1.01 193.3±0.75µs ? ?/sec query_get_many_2/50000_calls_table 1.00 243.9±0.55µs ? ?/sec 1.05 257.2±8.62µs ? ?/sec query_get_many_5/50000_calls_sparse 1.00 585.9±7.70µs ? ?/sec 1.03 600.6±5.99µs ? ?/sec query_get_many_5/50000_calls_table 1.00 673.7±7.44µs ? ?/sec 1.07 722.3±10.77µs ? ?/sec run_condition/no/1000_systems 1.00 23.7±0.06µs ? ?/sec 1.06 25.1±0.07µs ? ?/sec run_condition/no/100_systems 1.00 1460.5±4.28ns ? ?/sec 1.03 1510.1±3.69ns ? ?/sec run_condition/no/10_systems 1.00 201.5±0.53ns ? ?/sec 1.04 209.1±2.37ns ? ?/sec run_condition/yes/1000_systems 1.00 1225.7±22.58µs ? ?/sec 1.02 1253.7±24.90µs ? ?/sec run_condition/yes/100_systems 1.02 89.4±3.43µs ? ?/sec 1.00 88.0±3.96µs ? ?/sec run_condition/yes_using_query/1000_systems 1.00 1288.3±26.57µs ? ?/sec 1.03 1323.0±24.73µs ? ?/sec run_condition/yes_using_query/100_systems 1.00 108.8±2.51µs ? ?/sec 1.03 112.3±3.09µs ? ?/sec run_condition/yes_using_resource/100_systems 1.03 99.0±3.37µs ? ?/sec 1.00 96.2±4.80µs ? ?/sec run_empty_schedule/MultiThreaded 1.03 15.3±0.10ns ? ?/sec 1.00 14.9±0.03ns ? ?/sec run_empty_schedule/Simple 1.01 15.2±0.15ns ? ?/sec 1.00 15.0±0.25ns ? ?/sec sized_commands_0_bytes/10000_commands 1.57 52.6±0.41µs ? ?/sec 1.00 33.5±0.10µs ? ?/sec sized_commands_0_bytes/1000_commands 1.57 5.3±0.01µs ? ?/sec 1.00 3.4±0.00µs ? ?/sec sized_commands_0_bytes/100_commands 1.56 536.5±4.83ns ? ?/sec 1.00 343.6±1.12ns ? ?/sec sized_commands_12_bytes/10000_commands 1.22 63.0±0.53µs ? ?/sec 1.00 51.5±6.06µs ? ?/sec sized_commands_12_bytes/1000_commands 1.25 5.7±0.01µs ? ?/sec 1.00 4.6±0.05µs ? ?/sec sized_commands_12_bytes/100_commands 1.27 579.3±1.28ns ? ?/sec 1.00 455.4±0.85ns ? ?/sec sized_commands_512_bytes/10000_commands 1.11 248.4±85.81µs ? ?/sec 1.00 224.3±52.11µs ? ?/sec sized_commands_512_bytes/1000_commands 1.09 22.8±0.18µs ? ?/sec 1.00 21.0±0.17µs ? ?/sec sized_commands_512_bytes/100_commands 1.13 1852.2±11.21ns ? ?/sec 1.00 1635.3±4.91ns ? ?/sec spawn_commands/10000_entities 1.04 844.2±11.96µs ? ?/sec 1.00 811.5±13.25µs ? ?/sec spawn_commands/1000_entities 1.05 84.9±3.66µs ? ?/sec 1.00 80.5±4.13µs ? ?/sec spawn_commands/100_entities 1.06 8.6±0.12µs ? ?/sec 1.00 8.1±0.12µs ? ?/sec spawn_world/10000_entities 1.03 413.2±25.20µs ? ?/sec 1.00 400.9±49.97µs ? ?/sec spawn_world/100_entities 1.02 4.1±0.62µs ? ?/sec 1.00 4.1±0.69µs ? ?/sec spawn_world/1_entities 1.04 42.2±3.23ns ? ?/sec 1.00 40.6±6.81ns ? ?/sec world_entity/50000_entities 1.18 88.3±0.42µs ? ?/sec 1.00 74.7±0.16µs ? ?/sec world_get/50000_entities_sparse 1.02 182.2±0.32µs ? ?/sec 1.00 179.5±0.84µs ? ?/sec world_get/50000_entities_table 1.01 198.3±0.46µs ? ?/sec 1.00 196.2±0.63µs ? ?/sec world_query_for_each/50000_entities_sparse 1.00 32.7±0.12µs ? ?/sec 1.01 33.1±0.46µs ? ?/sec ``` </details> This roughly doubles command spawning speed! Despawning also sees a 20-30% improvement. Dummy commands improve by 10-50% (due to not needing an entity flush). Other benchmarks seem to be noise and are negligible. It looks to me like a massive performance win! --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Christian Hughes <9044780+ItsDoot@users.noreply.github.com> Co-authored-by: urben1680 <55257931+urben1680@users.noreply.github.com> Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> Co-authored-by: Trashtalk217 <24552941+Trashtalk217@users.noreply.github.com> Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
|
|
a264dd3c85 |
API for traversing Relationships and RelationshipTargets in dynamic contexts (#21601)
# Objective Currently there is no way to traverse relationships in type-erased contexts or to define dynamic relationship components, which is a hole in the current relationships api. ## Solution Introduce `RelationshipAccessor` to describe a way to get `Entity` values from any registered relationships in dynamic contexts and store it on `ComponentDescriptor`. This allows to traverse relationships without knowing their type, which is useful for working with entity hierarchies using non-default components. ## Testing Added a simple test/example of how to use this api to traverse hierarchies in a type-erased context. |
||
|
|
1610aa9d91 |
Remove FontAtlasSets (#21345)
# Objective The `FontAtlasSets` resource contains a map from `AssetId<Font>`s to `FontAtlasSet`s. `FontAtlasSet` has another map from a `FontAtlasKey` (tuple of the font size bit cast into a `u32` and `FontSmoothing`) to a vec that contains the actual font atlases for the font. The redirection through the additional map doesn't serve much purpose, only individual fonts are looked up (except when freeing unused fonts), not the set of all atlases for a particular font face. ## Solution * Remove `FontAtlasSet`. * Rename `FontAtlasSets` to `FontAtlasSet`. * Add `AssetId<Font>` to `FontAtlasKey`. * Change the font atlas map to a `HashMap<FontAtlasKey, Vec<FontAtlas>>` * Move the `FontAtlasSet` methods `add_glyph_to_atlas`, `get_glyph_atlas_info`, and `get_outlined_glyph_texture` into the `font_atlas` module and rework them into free functions. * Change `FontAtlasSet` into a newtype around the map and remove the manual method implementations in favour of deriving `Deref` and `DerefMut`. We could consider renaming `FontAlasSet` to `FontAtlasMap`, but it doesn't seem necessary, and mathematically a map is a set so it's not incorrect. ## Testing The output from all of the text examples should be unchanged. Naive benchmarks suggest this is a modest improvement in performance at a very high load (5% ish), but this is more about reducing complexity to make more significant optimisations possible. Freeing the unused atlases when a font asset is removed will be slower, it has to filter all the font atlas vecs now instead of just removing the entry for the font asset, but this isn't done very often and the number of entries should be relatively low. --------- Co-authored-by: Dimitrios Loukadakis <dloukadakis@users.noreply.github.com> |
||
|
|
acae6a5c16 |
Add a WinitSettings::continuous constructor (#20754)
# Objective * Add a constructor to `WinitSettings` for the continuous update mode used by the stress tests. ## Solution * Add the constructor. --------- Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
eda118d033 |
Event Rearchitecture (#20731)
There is general consensus that our terminology for Events, "entity events", Observers, and BufferedEvents needs clarity. Additionally, many of us also agree that the current Observer system would benefit from additional static-ness: currently it is assumed that you can use events in pretty much any context, and they all go through the exact same code path. Alice put forth a proposal to [Overhaul Observers](https://hackmd.io/@bevy/rk4S92hmlg), and we have already partially implemented it for 0.17. I think it does a great job of outlining many of the issues at play, and it solves them reasonably well. But I _also_ think the proposed solution isn't yet ideal. Given that it is already partially implemented for 0.17, it is a breaking change, _and_ given that we have already broken the Observer API a number of times, I think we need to sort this out before the next release. This is a big changeset, but it is _largely_ just a reframing of what is already there. I haven't fundamentally changed the behaviors. I've just refined and constrained in a way that allows us to do what we are currently doing in a clearer, simpler, and more performant way. First, I'll give some quick notes on Alice's proposal (which you all should read if you haven't yet!): ### Notes on Alice's Proposal - I like the move toward a more static API - I think we've gone too far down the "separate terminology" path. The proposal introduces a zoo of apis, terms, and "subterms". I think we need to simplify our concepts and names to make this all easier to talk about and use in practice. - BroadcastEvent feels like the wrong name. EntityEvent is also "broadcast" in the exact same way - BufferedEvent is a completely different system than EntityEvent and BroadcastEvent. This muddles concepts too much. It needs its own standalone, single-word concept name. - "Universal observers": I think this should be fully context driven, rather than needing encoding in the API. - I agree we can't get rid of buffered events, and that merging them with "broadcast events" isn't helpful - I'm not quite sure how we'd make the proposed PropagateEvent subtrait work transparently. This can't be "layered on top" as a trait. It needs to be baked in at more fundamental level. * I don't like `app.add_broadcast_observers()`, `app.add_universal_observers()`, `Observer::entity_observer`, `Observer::broadcast`, etc. The `On` event should statically determine whether an observer is an "entity observer" or a "broadcast" Observer. This would already be encoded in the type system and is therefore something we can do on the developer's behalf. Likewise, any observer being registered at a top level is inherently _not_ a specific entity observer. All of these variants serve to make users guess and poke around in a way that is unnecessary. I want simple one word concept names, single constructors, etc. ### Proposed Principals - Static-ness: - Events should only be usable in the context they were defined to be used. - When triggered, Observers should *only* have access to fields and behaviors that are relevant: - Dont return Option or PLACEHOLDER: the field or function shouldn't exist - Entity events that don't support propagation shouldn't expose that functionality - Don't do unnecessary work at runtime - Event triggers shouldn't branch through every potential event code path - Don't clone potentially large lists of event context unnecessarily (Ex: we currently clone the component list for every observer invocation) - Minimize codegen - Don't recompile things redundantly. - Don't compile unnecessary code paths. - Clear and Simple - Minimize the number of concept names floating around, and lock each concept down heavily to a specific context - I'm convinced at this point that "buffered events" and "observer events" sharing concept names is wrong. We need two clean and clear terms, and I'm willing to give "buffered events" a slightly worse name if it means "observer events" can be nicer. - Don't throw the concept name "Event" out ... it is a very good name. Instead, constrain it to one specific thing. - Minimize our API surface - Events contain all context, including what used to be the "target". This lets people define the "target" name that makes the most sense for the context, and lets the documentation fully describe the context of that "target". ### Concepts - **Event** (the thing you "observe") - Rationale: "Event" is the clear choice for this concept. An "event" feels like something that happens in real time. "Event observers" are things that observe events when they occur (are triggered). Additionally, this is the concept that "propagates", and "event propagation" is a term people understand. - **Trigger**: (the verb that "causes" events to happen for targets). Events are Triggered. This can include additional context/ data that is passed to observers / informs the trigger behavior. Events have _exactly_ one Trigger. If you want a different trigger behavior, define a new event. This makes the system more static, more predictable, and easier to understand and document. `world.trigger_ref_with` makes it possible to pass in mutable reference to your own Trigger data, making it possible to customize the input trigger data and read out the final trigger data. - **Observer** (the thing that "observes" events): An event's `Trigger` determines which observers will run. - **Event Types**: You can build any "type" of event. The concept of a "target" has been removed. Instead, define a `Trigger` that expects a specific kind of event (ex: `E: EntityEvent`). - **EntityEvent** We add a new `EntityEvent` trait, which defines an `event.entity()` accessor. This is used by the `Trigger` impls : `EntityTrigger`, `PropagateEntityTrigger`, and `EntityComponentsTrigger`. - **Message** (the buffered thing you "read" and "write") - `Message` is a solid metaphor for what this is ... it is data that is written and then at some later point read by someone / something else. I expect existing consumers of "buffered events" to lament this name change, as "event" feels nicer. But having a separate name is within everyone's best interest. - **MessageReader** (the thing that reads messages) - **MessageWriter** (the thing that writes messages) ### The Changes - `Event` trait changes - Event is now used exclusively by Observers - Added `Event::Trigger`, which defines what trigger implementation this event will use - Added the `Trigger` trait - All of the shared / hard-coded observer trigger logic has been broken out into individual context-specific Trigger traits. - "Trigger Targets" have been removed. - Instead, Events, in combination with their Trigger impl, decide how they will be triggered. In general, this means that Events now include their "targets" as fields on the event. - APIs like `trigger_targets` have been replaced by `trigger`, which can now be used for any `Event` - `EntityEvent` trait changes - Propagation config has been removed from the `EntityEvent` trait. It now lives on the `Trigger` trait (specifically the `PropagateEntityTrigger` trait). - `EntityEvent` now provides `entity / entity_mut` accessors for the Event it is implemented for - `EntityEvent` defaults to having no propagation (uses the simpler `EntityTrigger`) - `#[entity_event(propagate)]` enables the "default" propagation logic (uses ChildOf). The existing `#[entity_event(traversal = X)]` has been renamed to `#[entity_event(propagate = X)` - Deriving `EntityEvent` requires either a single `MyEvent(Entity)`, the `entity` field name (`MyEvent { entity: Entity}`), or `MyEvent { #[event_entity] custom: Entity }` - Animation event changes - Animation events now have their own `AnimationEvent` trait, which sets the `AnimationEventTrigger`. This allows developers to pass in events that _dont_ include the Entity field (as this is set by the system). The custom trigger also opens the doors to cheaply passing in additional animation system context, accessible through `On` - `EntityComponentsTrigger` - The built in Add/Remove/etc lifecycle events now use the `EntityComponentsTrigger`, which passes in the components as additional state. This _significantly_ cuts down on clones, as it does a borrow rather than cloning the list into _each_ observer execution. - Each event now has an `entity` field. - Style changes - Prefer the event name for variables: `explode: On<Explode>` not `event: On<Explode>` - Prefer using the direct field name for the entity on entity events, rather than `event.entity()`. This allows us to use more specific names where appropriate, provides better / more contextual docs, and coaches developers to think of `On<MyEvent>` _as_ the event itself. Take a look at the changes to the examples and the built-in events to see what this looks like in practice. ### Downsides - Moving the "target" into the event adds some new constraints: - Triggering the same event for multiple entities requires multiple trigger calls. For "expensive" events (ex: lots of data attached to the event), this will be more awkward. Your options become: - Create multiple instances of the event, cloning the expensive data - Use `trigger_ref`, and mutate the event on each call to change the target. - Move the "expensive" shared data into the Trigger, and use `trigger_ref_with`` - We could build a new EntityEvent method that abstracts over the "event mutation" behavior and provides something like the old `trigger_target` behavior. - Use a different `EntityTargetTrigger` (not currently provided by bevy, but we could), which brings back the old behavior. This would be used with `trigger_with` to replicate the old pattern: `world.trigger_with(MyEvent, [e1, e2].into())` (or we could make the `into()` implicit) - Bubbling the event involves mutating the event to set the entity. This means that `trigger_ref` will result in the event's `EntityEvent::entity()` being the final bubbled entity instead of the initial entity. - Some APIs (trivially) benefit from the "target entity" being separate from the event. Specifically, this new API requires changes to the "Animation Event" system in AnimationPlayer. I think this is actually a good change set, as it allows us to: - Cheaply expose more animation state as part of a new AnimationEventTrigger impl - Move that "implict" entity target provided by the AnimationPlayer into the AnimationEventTrigger - Encode the "animation event trigger-ness" of the event into the type itself (by requiring `#[event(trigger = AnimationEventTrigger)]`) - By not implementing Default for AnimationEventTrigger, we can block animation events from being fired manually by the user. ### Draft TODO - [x] Fill in documentation and update existing docs - [ ] Benchmark: I expect this impl to be significantly faster. There might also be tangible binary size improvements, as I've removed a lot of redundant codegen. - [x] Update release notes and migration guides ### Next Steps - The `BufferedEvent -> Message` rename was not included to keep the size down. Fixes #19648 --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Jan Hohenheim <jan@hohenheim.ch> |
||
|
|
13877fa84d |
Add a new trait to accept more types in the Val-helper functions (#20551)
# Objective - Allow the `Val`-helper functions to accept more types besides just `f32` Fixes #20549 ## Solution - Adds a new trait that can be implemented for numbers - That trait has a method that converts `self` to `f32` ## Testing - I tested it using Rust's testing framework (although I didn't leave the tests in, as I don't deem them important enough) <details> <summary>Rust test</summary> ```rust #[cfg(test)] mod tests { use super::*; #[test] fn test_val_helpers_work() { let p = px(10_u8); assert_eq!(p, Val::Px(10.0)); let p = px(10_u16); assert_eq!(p, Val::Px(10.0)); let p = px(10_u32); assert_eq!(p, Val::Px(10.0)); let p = px(10_u64); assert_eq!(p, Val::Px(10.0)); let p = px(10_u128); assert_eq!(p, Val::Px(10.0)); let p = px(10_i8); assert_eq!(p, Val::Px(10.0)); let p = px(10_i16); assert_eq!(p, Val::Px(10.0)); let p = px(10_i32); assert_eq!(p, Val::Px(10.0)); let p = px(10_i64); assert_eq!(p, Val::Px(10.0)); let p = px(10_i128); assert_eq!(p, Val::Px(10.0)); let p = px(10.3_f32); assert_eq!(p, Val::Px(10.3)); let p = px(10.6_f64); assert_eq!(p, Val::Px(10.6)); } } ``` </details> --- ## Showcase ```rust // Same as Val::Px(10.) px(10); px(10_u8); px(10.0); ``` |
||
|
|
950a9de4e6 |
Disable button labels for the many_buttons stress text by default (#20636)
# Objective The `many_buttons` benchmark isn't a good measure of UI performance because it's dominated by the text layout and rendering for the button labels. The button labels should be disabled by default, we have other text specific stress tests. ## Solution 1. Rename the `no_text` commandline parameter to `text`. The buttons are no longer shown by default, to run the example with labels you need to use: ``` cargo run --example many_buttons --release -- --text ``` 2. Add a second button icon image, this is to break up the batches during rendering without text. 3. Spawn every button with an icon. Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
99d511afa3 |
update some stress tests to use the common setup (#20751)
# Objective - `many_gradients`, `many_materials` and `many_text2d` don't always run at their full potential ## Solution - Use the same scale factor as other stress tests. - Use the same winit settings as other stress tests. |
||
|
|
5058f8a9e6 |
Improve On Terminology (#20648)
# Objective Fixes #19263 (and expands on it) Within `Observers`, we have started to distance ourselves from the "trigger" terminology. Specifically, we have renamed `Trigger` to `On`. I think this was a very good move, but we're currently in an awkward middle ground state. Users interact with `On` as if it were an event: `On<E>` exposes the event and derefs directly to it. I think we should embrace this mindset fully, in the interest of clarity. ## Solution - Rename all `trigger: On<SomeEvent>` cases to `event: On<SomeEvent>`. - Rename `On::target` to `On::entity`. This reads _much_ better when writing `query.get(event.entity())` and pairs more effectively with the `EntityEvent` terminology. - Rename `On::original_target` to `On::original_entity`, for the same reasons. - Take advantage of the `Deref` behavior where appropriate ```rust // Before entity.observe(|trigger: On<Explode>| { println!("{} exploded!", trigger.target()); }) // After entity.observe(|event: On<Explode>| { println!("{} exploded!", event.entity()); }) ``` |
||
|
|
121981b016 |
Remove redundant number conversion in window resolution (#20582)
# Objective `WindowResolution` stores the width and height as `u32`, but the constructors take `f32` and just convert straight to `u32`. Additionally, everywhere in Bevy where a `WindowResolution` is constructed specifies whole numbers, making the use of a float entirely pointless. ## Solution Replace the `f32` constructors with `u32` constructors. I also decided to change the generic `I: Into<f32>` tuple and array constructors to only take u32 instead of `I: Into<u32>` for UX reasons. It allows formatting those constructors as `(1920, 1080).into()`, as the compiler can infer that the numbers are u32. Keeping those impls generic prevents that inference, and because the default number type (i32) doesn't impl `Into<u32>`, that would require formatting it explicitly as `(1920u32, 1080u32).into()` to compile. In practice, these generic constructors were only used with whole-number f32 values anyway, like everything else, so them being generic wasn't actually leveraged anywhere. ## Testing Chased type errors until they were all gone |
||
|
|
9ee0aaafc5 |
Split out sprite rendering (#20587)
# Objective - Split out `bevy_sprite_render` from `bevy_sprite` . ## Solution - Do the thing ## Testing - CI - `cargo run --example sprite` |
||
|
|
e2a709e06d |
Update morph_targets and many_foxes examples to use observers (#20531)
## Objective Change some examples to follow best practices for playing animations, and as a bonus work around an issue with scenes spawning multiple times. ## Background Examples that play skeletal animations usually have two steps: 1) Start scene spawning. 2) Play animations after the scene has spawned. Different examples use different approaches for triggering part 2, including a scene spawning observer and `Added<AnimationPlayer>` queries. The observer approach is arguably best as it's more tightly scoped and easier for users to extend. The other approaches work in simple examples but fall down when users want multiple scenes or animations. See #17421 for more detail. As a bonus, the scene spawning observer works around a current issue with scenes spawning multiple times - see #20393, #20430. Although there's an argument that this PR shouldn't land until the issue is properly fixed, as these examples are a useful test case. ## Solution Update the `morph_targets` and `many_foxes` examples to use observers. I also made a few tweaks and fixes to `morph_targets`. - Fix documentation referring to an `update_weights` feature that isn't in the example. - Use the same `AnimationToPlay` component as the `animated_mesh` example. - Change the `name_morphs` system to be event driven and print the asset name. - This is maybe too complex, but could also be nice for users to c&p into their app for debugging. I haven't updated the `animated_mesh_control`, `animated_mesh_events`, and `animation_masks` examples, which still use `Added<AnimationPlayer>`. ## Testing ```sh cargo run --example morph_targets cargo run --example many_foxes ``` |
||
|
|
68b848217f |
Yeet RenderAssetUsages re-export (#20498)
# Objective - Forgor to yeet this ## Solution - rember and yeet ## Testing cargo check --examples --all-features |
||
|
|
ce416c37a2 |
Use bevy::light in examples instead of bevy::pbr::light re-export (#20487)
# Objective - Prepare for removing re-exports ## Solution - title ## Testing - cargo check --examples --all-features |
||
|
|
03dd839b82 |
Use bevy::camera in examples instead of bevy::render::camera re-export (#20477)
# Objective - Prepare for removing re-exports ## Solution - title ## Testing - cargo check --examples |
||
|
|
ef845e0cea |
Update rand, glam and encase to latest versions (#18047)
# Objective New `rand` version, which means updating `glam` and `encase` to support the newer ecosystem update. Does mean that this changes how WASM builds need to be done in order to configure `getrandom` correctly, but this can be remedied with updated docs. ## Solution Updating all needed dependencies to their compatible versions. ~~This PR is currently blocked by `encase`, which is waiting on [this PR](https://github.com/teoxoy/encase/pull/88) to be merged and then a new version published.~~ ~~This PR is no longer blocked~~, ~~`hexasphere` is blocking this PR now due to not yet having a new release with the latest `glam` version support~~, The PR is all good to go now, everything in order across glam/rand deps. ## Testing - Must pass CI for all checks, tests, not introduce breaking changes. --- ## Migration Guide With newer versions of `glam` & `encase`, the updated versions don't seem to have introduced breakages, though as always, best to consult their docs [1](https://docs.rs/glam/latest/glam/) [2](https://docs.rs/encase/0.11.0/encase/) for any changes. `rand` changes are more extensive, with changes such as `thread_rng()` -> `rng()`, `from_entropy()` -> `from_os_rng()`, and so forth. `RngCore` is now split into infallible `RngCore` and fallible `TryRngCore`, and the `distributions` module has been renamed to `distr`. Most of this affects only internals, and doesn't directly affect Bevy's APIs. For the full set of changes, see `rand` [migration notes](https://rust-random.github.io/book/update-0.9.html). `getrandom` is also updated, and will require additional configuration when building Bevy for WASM/Web (if also using `rand`). The full details of how to do this is in the `getrandom` docs [1](https://github.com/rust-random/getrandom?tab=readme-ov-file#opt-in-backends) [2](https://github.com/rust-random/getrandom?tab=readme-ov-file#webassembly-support). --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
|
|
3fc49f00c1 |
Preconvert colors before sending to shader (#20074)
# Objective - Fixes #20008 - Preconvert colors before sending them to the UI gradients shader for better performance ## Solution - Modified `prepare_gradient` in `gradient.rs` to convert colors from `LinearRgba` to `Srgba` on the CPU before sending to the GPU - Updated the gradient shader to remove per-pixel color space conversions since colors are now pre-converted - Added documentation to clarify that vertex colors are in sRGB space This optimization reduces the number of power operations per pixel from 3 to 1: - **Before**: Convert start color to sRGB, convert end color to sRGB, mix, convert back to linear (3 pow operations per pixel) - **After**: Colors pre-converted on CPU, mix in sRGB space, convert back to linear (1 pow operation per pixel) ## Testing - Verified that the UI gradient examples (`cargo run --example gradients`) compile and render correctly - The visual output should remain identical while performance improves, especially for large gradient areas - Changes maintain the same color interpolation behavior (mixing in sRGB space) To test: 1. Run `cargo run --example gradients` or `cargo run --example stacked_gradients` 2. Verify gradients render correctly --------- Co-authored-by: ickshonpe <david.curthoys@googlemail.com> |
||
|
|
b62b14c293 |
Add UVec to_extents helper method (#19807)
# Objective - Simplify common usecase ## Solution - Helper trait |
||
|
|
92e65d5eb1 | Upgrade to Rust 1.88 (#19825) | ||
|
|
c549b9e9a4 |
Copy stress test settings for many_camera_lights (#19512)
# Objective - Fixes #17183 ## Solution - Copied the stress settings from the `many_animated_sprite` example that were mentioned in the ticket ## Testing - Run the example Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
02fa833be1 |
Rename JustifyText to Justify (#19522)
# Objective Rename `JustifyText`: * The name `JustifyText` is just ugly. * It's inconsistent since no other `bevy_text` types have a `Text-` suffix, only prefix. * It's inconsistent with the other text layout enum `Linebreak` which doesn't have a prefix or suffix. Fixes #19521. ## Solution Rename `JustifyText` to `Justify`. Without other context, it's natural to assume the name `Justify` refers to text justification. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
571b3ba475 |
Remove ArchetypeComponentId and archetype_component_access (#19143)
# Objective Remove `ArchetypeComponentId` and `archetype_component_access`. Following #16885, they are no longer used by the engine, so we can stop spending time calculating them or space storing them. ## Solution Remove `ArchetypeComponentId` and everything that touches it. The `System::update_archetype_component_access` method no longer needs to update `archetype_component_access`. We do still need to update query caches, but we no longer need to do so *before* running the system. We'd have to touch every caller anyway if we gave the method a better name, so just remove `System::update_archetype_component_access` and `SystemParam::new_archetype` entirely, and update the query cache in `Query::get_param`. The `Single` and `Populated` params also need their query caches updated in `SystemParam::validate_param`, so change `validate_param` to take `&mut Self::State` instead of `&Self::State`. |