mirror of
https://github.com/bevyengine/bevy.git
synced 2026-07-01 00:05:45 -04:00
create-pull-request/patch
87 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4dc8fb4919 |
Change the ambiguity_detection test to be more strict. (#23846)
# Objective - Fixes #23843 by preventing these systems from recurring inside Bevy. - Note this does not fix this for users of Bevy. They would need to setup their own "strict" checking. ## Solution - Disable `auto_insert_apply_deferred` in the `ambiguity_detection` example. Now these stages won't accidentally resolve these ambiguities, so they will be detected! - Stop running the app in `ambiguity_detection` and just manually initialize the schedules. - We have to do this otherwise the schedules just panic because they depend on various resource initializations to be executed by commands (which were previously being applied by the automatically inserted `apply_deferred`). ### Caveats: - The ambiguity_detection CI test now won't detect ambiguities in runtime-added systems. - This is not a pattern we use today, and I'd encourage us to **delete** systems instead so that tooling can grab the systems before the app runs. - To clarify, this is **not** ambiguity detection in general - just Bevy's CI test that is affected. ## Testing - Ran the examples and all the ambiguities have been fixed in previous PRs. |
||
|
|
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. |
||
|
|
380d2c35f8 |
Enable RenderApp Ambiguity Detection (#22954)
# Objective - Fix #7386 ## Solution - #22951 #22952 #22949 #22945 and a handful more changes on this PR. NOTE: Ci will not pass until all these other prs are merged. - Fix a bug ## Testing - examples look fine --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
|
|
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>
|
||
|
|
9d2db8838f |
Improve frustum culling of skinned meshes through per-joint bounds (#21837)
## Objective Mostly fix #4971 by adding a new option for updating skinned mesh `Aabb` components from joint transforms. https://github.com/user-attachments/assets/c25b31fa-142d-462b-9a1d-012ea928f839 This fixes cases where vertex positions are only modified through skinning. It doesn't fix other cases like morph targets and vertex shaders. The PR kind of upstreams [`bevy_mod_skinned_aabb`](https://github.com/greeble-dev/bevy_mod_skinned_aabb), but with some changes to make it simpler and more reliable. ### Dependencies - (MERGED) #21732 (or something similar) is desirable to make the new option work with `RenderAssetUsages::RENDER_WORLD`-only meshes. - This PR is authored as if 21732 has landed. But if that doesn't happen then I can adjust this PR to note the limitation. - (Optional) #21845 adds an option related to skinned mesh bounds. - Either PR can land first - the second will need to be updated. ## Background If a main world entity has a `Mesh3d` component then it's automatically assigned an `Aabb` component. This is done by `bevy_camera` or `bevy_gltf`. The `Aabb` is used by `bevy_camera` for frustum culling. It can also be used by `bevy_picking` as an optimization, and by third party crates. But there's a problem - the `Aabb` can be wrong if something changes the mesh's vertex positions after the `Aabb` is calculated. This can be done by vertex shaders - notably skinning and morph targets - or by mutating the `Mesh` asset (#4294). For the skinning case, the most common solution has been to disable frustum culling via the `NoFrustumCulling` component. This is simple, and might even be the most efficient approach for apps where meshes tend to stay on-screen. But it's annoying to implement, bad for apps where meshes are often off-screen, and it only fixes frustum culling - it doesn't help other systems that use the `Aabb`. ## Solution This PR adds a reliable and reasonably efficient method of updating the `Aabb` of a skinned mesh from its animated joint transforms. See the "How does it work" section for more detail. The glTF loader can add skinned bounds automatically if a new `GltfSkinnedMeshBoundsPolicy` option is enabled in `GltfPlugin` or `GltfLoaderSettings`: ```rust app.add_plugins(DefaultPlugins.set(GltfPlugin { skinned_mesh_bounds_policy: GltfSkinnedMeshBoundsPolicy::Dynamic, ..default() })) ``` _The new glTF loader option is enabled by default_. I think this is the right choice for several reasons: - Bugs caused by skinned mesh culling have been a regular pain for both new and experienced users. Now the most common case Just Works(tm). - The CPU cost is modest (see later section), and sophisticated users can opt-out. - GPU limited apps might see a performance increase if the user was previously disabling culling. Non-glTF cases require some manual steps. The user must ask `Mesh` to generate the skinned bounds, and then add the `DynamicSkinnedMeshBounds` marker component to their mesh entity. ```rust mesh.generate_skinned_mesh_bounds()?; let mesh_asset = mesh_assets.add(mesh); entity.insert((Mesh3d(mesh_asset), DynamicSkinnedMeshBounds)); ``` See the `custom_skinned_mesh` example for real code. ## Bonus Features ### `GltfSkinnedMeshBoundsPolicy::NoFrustumCulling` This is a convenience for users who prefer the `NoFrustumCulling` workaround, but want to avoid the hassle of adding it after a glTF scene has been spawned. ```rust app.add_plugins(DefaultPlugins.set(GltfPlugin { skinned_mesh_bounds_policy: GltfSkinnedMeshBoundsPolicy::NoFrustumCulling, ..default() })) ``` PR #21845 is also adding an option related to skinned mesh bounds. I'm fine if that PR lands first - I'll update this PR to include the option. ### Gizmos `bevy_gizmos::SkinnedMeshBoundsGizmoPlugin` can draw the per-joint AABBs. ```rust fn toggle_skinned_mesh_bounds(mut config: ResMut<GizmoConfigStore>) { config.config_mut::<SkinnedMeshBoundsGizmoConfigGroup>().1.draw_all ^= true; } ``` The name is debatable. It's not technically drawing the bounds of the skinned mesh - it's drawing the per-joint bounds that contribute to the bounds of the skinned mesh. ## Testing ```sh cargo run --example test_skinned_mesh_bounds # Press `B` to show mesh bounds, 'J' to show joint bounds. cargo run --example scene_viewer --features "free_camera" -- "assets/models/animated/Fox.glb" cargo run --example scene_viewer --features "free_camera" -- "assets/models/SimpleSkin/SimpleSkin.gltf" # More complicated mesh downloaded from https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main/Models/RecursiveSkeletons cargo run --example scene_viewer --features "free_camera" -- "RecursiveSkeletons.glb" cargo run --example custom_skinned_mesh ``` I also hacked `custom_skinned_mesh` to simulate awkward cases like rotated and off-screen entities. ## How Does It Work? <details><summary>Click to expand</summary> ### Summary `Mesh::generated_skinned_mesh_bounds` calculates an AABB for each joint in the mesh - the AABB encloses all the vertices skinned to that joint. Then every frame, `bevy_camera::update_skinned_mesh_bounds` uses the current joint transforms to calculate an `Aabb` that encloses all the joint AABBs. This approach is reliable, in that the final `Aabb` will always enclose the skinned vertices. But it can be larger than necessary. In practice it's tight enough to be useful, and rarely more than 50% bigger. This approach works even with non-rigid transforms and soft skinning. If there's any doubt then I can add more detail. ### Awkward Bits The solution is not as simple and efficient as it could be. #### Problem 1: Joint transforms are world-space, `Aabb` is entity-space. - Ideally we'd use the world-space joint transforms to calculate a world-space `Aabb`, but that's not possible. - The obvious solution is to transform the joints to entity-space, so the `Aabb` is directly calculated in entity-space. - But that means an extra matrix multiply per joint. - This PR calculates the `Aabb` in world-space and then transforms it to entity-space. - That avoids a matrix multiply per-joint, but can increase the size of the `Aabb`. #### Problem 2: Joint AABBs are in a surprising(?) space. - When creating joint AABBs from a mesh, the intuitive solution would be to calculate them in joint-space. - Then the update just has to transform them by the world-space joint transform. - But to calculate them in joint-space we need both the bind pose vertex positions and the bind pose joint transforms. - These two parts are in separate assets - `Mesh` and `SkinnedMeshInverseBindposes` - and those assets can be mixed and matched. - So we'd need to calculate a `SkinnedMeshBoundsAsset` for each combination of `Mesh` and `SkinnedMeshInverseBindposes`. - (`bevy_mod_skinned_aabb` uses this approach - it's slow and fragile.) - This PR calculates joint AABBs in *mesh-space* (or more strictly speaking: bind pose space). - That can be done with just the `Mesh` asset. - One downside is that the update needs an extra matrix multiply so we can go from mesh-space to world-space. - However, this might become a performance advantage if frustum culling changes - see the "Future Options" section. - Another minor downside is that mesh-space AABBs (red in the screenshot below) tend to be bigger than joint-space AABBs (green), since joints with one long axis might be at an awkward angle in mesh-space. <img width="1085" height="759" alt="image" src="https://github.com/user-attachments/assets/a02a28c3-8882-412c-9be1-64109b767da7" /> ### Future Options For frustum culling there's a cheeky way to optimize and simplify skinned bounds - put frustum culling in the renderer and calculate a world-space AABB during `extract_skins`. The joint transform will be already loaded and in the right space, so we can avoid an entity lookup and matrix multiply. I estimate this would make skinned bounds 3x faster. Another option is to change main world frustum culling to use a world-space AABB. So there would be a new `GlobalAabb` component that gets updated each frame from `Aabb` and the entity transform (which is basically the same as transform propagation and the relationship between `Transform` and `GlobalTransform`). This has some advantages and disadvantages but I won't get into them here - I think putting frustum culling into the renderer is a better option. (Note that putting frustum culling into the renderer doesn't mean removing the current main world visibility system - it just means the main world system would be separate opt-in system) </details> ## Performance <details><summary>Click to expand</summary> ### Initialization Creating the skinned bounds asset for `Fox.glb` (576 verts, 22 skinned joints) takes **0.03ms**. Loading the whole glTF takes 8.7ms, so this is a **<1% increase**. ### Per-Frame The `many_foxes` example has 1000 skinned meshes, each with 22 skinned joints. Updating the skinned bounds takes **0.086ms**. This is a throughput of roughly 250,000 joints per millisecond, using two threads. <img width="2404" height="861" alt="image" src="https://github.com/user-attachments/assets/c27165ae-dc6c-4f6b-bbfb-4e211ab0263c" /> The whole animation update takes 3.67ms (where "animation update" = advancing players + graph evaluation + transform propagation). So we can kinda sorta claim that this PR increases the cost of skinned animation by roughly **3%**. But that's very hand-wavey and situation dependent. This was tested on an AMD Ryzen 7900 but with `TaskPoolOptions::with_num_threads(6)` to simulate a lower spec CPU. Comparing against a few other threading options: - Non-parallel: **0.141ms**. - 6 threads (2 compute threads): **0.086ms**. - 24 threads (15 compute threads): **0.051ms**. So the parallel iterator is better but quickly hits diminishing returns as the number of threads increases. ### Future Options The "How Does It Work" section mentions moving skinned mesh bounds into the renderer's skin extraction. Based on some microbenchmarks, I estimate this would reduce non-parallel `many_foxes` from 0.141ms to 0.049ms, so roughly 3x faster. Requiring AVX2 (to enable broadcast loads) or pre-splatting (to fake broadcast loads for SSE) would knock off another 25%. And fancier SIMD approaches could do better again. There's also approaches that trade reliability for performance. For character rigs, an effective optimization is to fold face and finger joints into a single bound on the head and hand joints. This can reduce the number of joints required by 50-80%. </details> ## FAQ <details><summary>Click to expand</summary> #### Why can't it be automatically added to any mesh? Then the glTF importer and custom mesh generators wouldn't need special logic. `bevy_mod_skinned_aabb` took the automatic approach, and I don't think the outcome was good. It needs some surprisingly fiddly and fragile logic to decide when an entity has the right combination of assets in the right loaded state. And it can never work with `RenderAssetUsages::RENDER_WORLD`. So this PR takes a more modest and manual approach. I think there's plenty of scope to generalise and automate as the asset pipeline matures. If the glTF importer becomes a purer glTF -> BSN transform, then adding skinned bounds could be a general scene/asset transform that's shared with other importers and custom mesh generators. #### Why is the data in `Mesh`? Shouldn't it go in `SkinnedMesh` or `SkinnedMeshInverseBindposes`? That might seem intuitive, but it wouldn't work in practice - the data is derived from `Mesh` alone. `SkinnedMesh` doesn't work because it's per mesh instance, so the data would be duplicated. `SkinnedMeshInverseBindposes` doesn't work because it can be shared between multiple meshes. The names are a bit misleading - `Mesh` does contain some skinning data, while `SkinnedMesh` and `SkinnedMeshInverseBindposes` are more like joint bindings one step removed from the vertex data. #### Why not put the bounds on the joint entities? This is surprisingly tricky in practice because multiple meshes can be bound to the same joint entity. So there would need to be logic that tracks the bindings and updates the bounds as meshes are added and removed. #### Why is the `DynamicSkinnedMeshBounds` component required? It's an optimisation for users who want to opt out. It might also be useful for future expansion, like adding options to approximate the bounds with an AABB attached to a single joint. #### Why are the update system and `DynamicSkinnedMeshBounds` component in `bevy_camera`? Shouldn't they be in `bevy_mesh`? `bevy_camera` is the owner and main user of `Aabb`, and already has some mesh related logic (`calculate_bounds` automatically adds an `Aabb` to mesh entities). So putting it in `bevy_camera` is consistent with the current structure. I'd agree that it's a little awkward though and could change in future. </details> ## What Do Other Engines Do? <details><summary>Click to expand</summary> - **Unreal**: Automatically uses [collision shapes](https://dev.epicgames.com/documentation/en-us/unreal-engine/physics-asset-editor-in-unreal-engine) attached to joints, which is similar to this PR in practice but fragile and inefficient. Also supports various fixed bounds options. - **Unity**: Fixed bounds attached to the root bone. Automatically calculated from animation poses or specified manually ([documentation](https://docs.unity3d.com/6000.4/Documentation/Manual/troubleshooting-skinned-mesh-renderer-visibility.html)). - **Godot**: Appears to use roughly the same method as this PR, although I didn't 100% confirm. See [`MeshStorage::mesh_get_aabb`](https://github.com/godotengine/godot/blob/fafc07335bdecacd96b548c4119fbe1f47ee5866/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp#L650) and [`RendererSceneCull::_update_instance_aabb`](https://github.com/godotengine/godot/blob/235a32ad11f40ecba26d6d9ceea8ab245c13adb0/servers/rendering/renderer_scene_cull.cpp#L1991). - **O3DE**: Fixed bounds attached to root bone, plus option to approximate the AABB from joint origins and a fudge factor. - **Northlight** (Remedy, Alan Wake 2): Specifically for vegetation, calculates bounds from joint extents on GPU ([source](https://gdcvault.com/play/1034310/Large-Scale-GPU-Based-Skinning), slide 48) An approach that's been proposed several times for Bevy is copying Unity's "fixed AABB from animation poses". I think this is more complicated and less reliable than many people expect. More complicated because linking animations to meshes can often be difficult. Less reliable because it doesn't account for ragdolls and procedural animation. But it could still be viable for for simple cases like a single self-contained glTF with basic animation. </details> --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.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> |
||
|
|
d431f7e139 |
Split AmbientLight into two (#21595)
# Objective For resources-as-components (#19731), structs mustn't doubly derive both `Component` and `Resource`. ## Solution Split `AmbientLight` in two: `AmbientLight` (the resource) and `AmbientLightOverride` (the component). ## Testing I initially made two structs `AmbientLightComponent` and `AmbientLightResource`, and replaced every mention with the relevant one to ensure that I didn't confuse the two. ## Notes - I don't know if the names are correct. I kept the easiest name for the resource, as that's the one most often used. - I haven't provided any conversion methods as there are already plans to replace the component variant with something else. --------- Co-authored-by: atlv <email@atlasdostal.com> |
||
|
|
4d74baf1ae |
BufferedEvent -> Message Rename (#20953)
This renames the concept of `BufferedEvent` to `Message`, and updates our APIs, comments, and documentation to refer to these types as "messages" instead of "events". It also removes/updates anything that considers messages to be "observable", "listenable", or "triggerable". This is a followup to https://github.com/bevyengine/bevy/pull/20731, which omitted the `BufferedEvent -> Message` rename for brevity. See that post for rationale. |
||
|
|
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> |
||
|
|
eed2e0ebe6 |
Fix missed RedrawRequested and WindowCloseRequested events with UpdateMode::Reactive (part 2) [ADOPTED] (#20624)
# Objective - Adopt and rebase #18549 ## Solution - merge conflicts were just if let chains ## Testing - example added by the PR works on mac --------- Co-authored-by: Matt Thompson <matt@boondocker.io> Co-authored-by: Aaron Loucks <aloucks@cofront.net> Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
|
|
f6b77e4e49 |
bevy_post_process (#20778)
# Objective Split out post process effects from bevy_core_pipeline because they are not core pipelines. ## Solution @IceSentry proposed something like this, not sure if the split is exactly as he envisioned but this seems reasonable to me. The goal is to move absolutely everything possible out of bevy_core_pipelines to unblock bevy_pbr/bevy_sprite_render compilation. Future PRs may attempt to move more little bits out. ## Testing |
||
|
|
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); ``` |
||
|
|
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 |
||
|
|
68b848217f |
Yeet RenderAssetUsages re-export (#20498)
# Objective - Forgor to yeet this ## Solution - rember and yeet ## 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 |
||
|
|
acc8f6d455 |
Stop using mesh re-exports in-repo and add bevy_mesh prelude (#20473)
# Objective - prepare to remove bevy_mesh re-export from bevy_render. This will be done in 0.18, but we might as well prepare for it now. ## Solution - Add a prelude and use bevy_mesh directly. After this pr and #20471, we will be ready. ## Testing - cargo check --examples |
||
|
|
9d4bfdbb53 |
Use RenderStartup for CopyDeferredLightingIdPipeline. (#20195)
# Objective - Progress towards #19887. - This migration actually caught a segfault on Linux + Vulkan + llvmpipe software rendering driver + (maybe) `xvfb-run` - It seems the reason for this is that we are concurrently terminating the program (which trigger's LLVMs `atexit` handlers) while we are also compiling pipelines in parallel. It seems LLVM code is not quite multithread safe in this situation. - Thanks to @kristoff3r for figuring out this segfault! - I still have no idea why this PR triggers it and not others. ## Solution Most of this is the same as all the other `RenderStartup` migrations. - Convert FromWorld impls to RenderStartup systems. In addition, I had to 1) enable synchronous pipeline compilation, 2) disable pipelined rendering in the `ambiguity_detection` test. This ensures that 1) the render thread just blocks on pipeline compilation rather than letting it run in another thread, 2) we don't leave `App::update` until the render thread finishes. So therefore, there won't be any pipeline compilation happening by the time the test ends. ## Testing - I ran the run-examples-linux-vulkan Github action many, many times and segfaults were consistent until this PR! Now they don't seem to happen anymore. |
||
|
|
4e9e78c31e |
Split BufferedEvent from Event (#20101)
# Objective > I think we should axe the shared `Event` trait entirely It doesn't serve any functional purpose, and I don't think it's useful pedagogically @alice-i-cecile on discord ## Solution - Remove `Event` as a supertrait of `BufferedEvent` - Remove any `Event` derives that were made unnecessary - Update release notes --------- Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net> |
||
|
|
3aed85a88b |
Rename send_event and similar methods to write_event (#20017)
Fixes: #18963 Follows up on: #17977 Adopts: #18966 In 0.16, `EventWriter::send` was renamed to `EventWriter::write`, but many methods were missed (sorry about that). This completes that refactor by renaming all `send` methods and internals. | Old | New | |-------------------------------------|--------------------------------------| | `World::send_event` | `World::write_event` | | `World::send_event_default` | `World::write_event_default` | | `World::send_event_batch` | `World::write_event_batch` | | `DeferredWorld::send_event` | `DeferredWorld::write_event` | | `DeferredWorld::send_event_default` | `DeferredWorld::write_event_default` | | `DeferredWorld::send_event_batch` | `DeferredWorld::write_event_batch` | | `Commands::send_event` | `Commmands::write_event` | | `Events::send` | `Events::write` | | `Events::send_default` | `Events::write_default` | | `Events::send_batch` | `Events::write_batch` | | `RemovedComponentEvents::send` | `RemovedComponentEvents::write` | | `command::send_event` | `commmand::write_event` | | `SendBatchIds` | `WriteBatchIds` | --------- Co-authored-by: shwwwa <shwwwa.dev@gmail.com> |
||
|
|
0f75142560 |
Add test for invalid skinned meshes (#18763)
## Objective Add a test that would have caught #16929 and #18712. ## Solution The PR adds a `test_invalid_skinned_mesh` example that creates various valid and invalid skinned meshes. This is designed to catch panics via CI, and can be inspected visually. It also tests skinned meshes + motion blur.  The screenshot shows all the tests, but two are currently disabled as they cause panics. #18074 will re-enable them. ### Concerns - The test is not currently suitable for screenshot comparison. - I didn't add the test to CI. I'm a bit unsure if this should be part of the PR or a follow up discussion. - Visual inspection requires understanding why some meshes are deliberately broken and what that looks like. - I wasn't sure about naming conventions. I put `test` in the name so it's not confused with a real example. ## Testing ``` cargo run --example test_invalid_skinned_mesh ``` Tested on Win10/Nvidia, across Vulkan, WebGL/Chrome, WebGPU/Chrome. |
||
|
|
38c3423693 |
Event Split: Event, EntityEvent, and BufferedEvent (#19647)
# Objective Closes #19564. The current `Event` trait looks like this: ```rust pub trait Event: Send + Sync + 'static { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } ``` The `Event` trait is used by both buffered events (`EventReader`/`EventWriter`) and observer events. If they are observer events, they can optionally be targeted at specific `Entity`s or `ComponentId`s, and can even be propagated to other entities. However, there has long been a desire to split the trait semantically for a variety of reasons, see #14843, #14272, and #16031 for discussion. Some reasons include: - It's very uncommon to use a single event type as both a buffered event and targeted observer event. They are used differently and tend to have distinct semantics. - A common footgun is using buffered events with observers or event readers with observer events, as there is no type-level error that prevents this kind of misuse. - #19440 made `Trigger::target` return an `Option<Entity>`. This *seriously* hurts ergonomics for the general case of entity observers, as you need to `.unwrap()` each time. If we could statically determine whether the event is expected to have an entity target, this would be unnecessary. There's really two main ways that we can categorize events: push vs. pull (i.e. "observer event" vs. "buffered event") and global vs. targeted: | | Push | Pull | | ------------ | --------------- | --------------------------- | | **Global** | Global observer | `EventReader`/`EventWriter` | | **Targeted** | Entity observer | - | There are many ways to approach this, each with their tradeoffs. Ultimately, we kind of want to split events both ways: - A type-level distinction between observer events and buffered events, to prevent people from using the wrong kind of event in APIs - A statically designated entity target for observer events to avoid accidentally using untargeted events for targeted APIs This PR achieves these goals by splitting event traits into `Event`, `EntityEvent`, and `BufferedEvent`, with `Event` being the shared trait implemented by all events. ## `Event`, `EntityEvent`, and `BufferedEvent` `Event` is now a very simple trait shared by all events. ```rust pub trait Event: Send + Sync + 'static { // Required for observer APIs fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } ``` You can call `trigger` for *any* event, and use a global observer for listening to the event. ```rust #[derive(Event)] struct Speak { message: String, } // ... app.add_observer(|trigger: On<Speak>| { println!("{}", trigger.message); }); // ... commands.trigger(Speak { message: "Y'all like these reworked events?".to_string(), }); ``` To allow an event to be targeted at entities and even propagated further, you can additionally implement the `EntityEvent` trait: ```rust pub trait EntityEvent: Event { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; } ``` This lets you call `trigger_targets`, and to use targeted observer APIs like `EntityCommands::observe`: ```rust #[derive(Event, EntityEvent)] #[entity_event(traversal = &'static ChildOf, auto_propagate)] struct Damage { amount: f32, } // ... let enemy = commands.spawn((Enemy, Health(100.0))).id(); // Spawn some armor as a child of the enemy entity. // When the armor takes damage, it will bubble the event up to the enemy. let armor_piece = commands .spawn((ArmorPiece, Health(25.0), ChildOf(enemy))) .observe(|trigger: On<Damage>, mut query: Query<&mut Health>| { // Note: `On::target` only exists because this is an `EntityEvent`. let mut health = query.get(trigger.target()).unwrap(); health.0 -= trigger.amount(); }); commands.trigger_targets(Damage { amount: 10.0 }, armor_piece); ``` > [!NOTE] > You *can* still also trigger an `EntityEvent` without targets using `trigger`. We probably *could* make this an either-or thing, but I'm not sure that's actually desirable. To allow an event to be used with the buffered API, you can implement `BufferedEvent`: ```rust pub trait BufferedEvent: Event {} ``` The event can then be used with `EventReader`/`EventWriter`: ```rust #[derive(Event, BufferedEvent)] struct Message(String); fn write_hello(mut writer: EventWriter<Message>) { writer.write(Message("I hope these examples are alright".to_string())); } fn read_messages(mut reader: EventReader<Message>) { // Process all buffered events of type `Message`. for Message(message) in reader.read() { println!("{message}"); } } ``` In summary: - Need a basic event you can trigger and observe? Derive `Event`! - Need the event to be targeted at an entity? Derive `EntityEvent`! - Need the event to be buffered and support the `EventReader`/`EventWriter` API? Derive `BufferedEvent`! ## Alternatives I'll now cover some of the alternative approaches I have considered and briefly explored. I made this section collapsible since it ended up being quite long :P <details> <summary>Expand this to see alternatives</summary> ### 1. Unified `Event` Trait One option is not to have *three* separate traits (`Event`, `EntityEvent`, `BufferedEvent`), and to instead just use associated constants on `Event` to determine whether an event supports targeting and buffering or not: ```rust pub trait Event: Send + Sync + 'static { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; const TARGETED: bool = false; const BUFFERED: bool = false; fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } ``` Methods can then use bounds like `where E: Event<TARGETED = true>` or `where E: Event<BUFFERED = true>` to limit APIs to specific kinds of events. This would keep everything under one `Event` trait, but I don't think it's necessarily a good idea. It makes APIs harder to read, and docs can't easily refer to specific types of events. You can also create weird invariants: what if you specify `TARGETED = false`, but have `Traversal` and/or `AUTO_PROPAGATE` enabled? ### 2. `Event` and `Trigger` Another option is to only split the traits between buffered events and observer events, since that is the main thing people have been asking for, and they have the largest API difference. If we did this, I think we would need to make the terms *clearly* separate. We can't really use `Event` and `BufferedEvent` as the names, since it would be strange that `BufferedEvent` doesn't implement `Event`. Something like `ObserverEvent` and `BufferedEvent` could work, but it'd be more verbose. For this approach, I would instead keep `Event` for the current `EventReader`/`EventWriter` API, and call the observer event a `Trigger`, since the "trigger" terminology is already used in the observer context within Bevy (both as a noun and a verb). This is also what a long [bikeshed on Discord](https://discord.com/channels/691052431525675048/749335865876021248/1298057661878898791) seemed to land on at the end of last year. ```rust // For `EventReader`/`EventWriter` pub trait Event: Send + Sync + 'static {} // For observers pub trait Trigger: Send + Sync + 'static { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; const TARGETED: bool = false; fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } ``` The problem is that "event" is just a really good term for something that "happens". Observers are rapidly becoming the more prominent API, so it'd be weird to give them the `Trigger` name and leave the good `Event` name for the less common API. So, even though a split like this seems neat on the surface, I think it ultimately wouldn't really work. We want to keep the `Event` name for observer events, and there is no good alternative for the buffered variant. (`Message` was suggested, but saying stuff like "sends a collision message" is weird.) ### 3. `GlobalEvent` + `TargetedEvent` What if instead of focusing on the buffered vs. observed split, we *only* make a distinction between global and targeted events? ```rust // A shared event trait to allow global observers to work pub trait Event: Send + Sync + 'static { fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } // For buffered events and non-targeted observer events pub trait GlobalEvent: Event {} // For targeted observer events pub trait TargetedEvent: Event { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; } ``` This is actually the first approach I implemented, and it has the neat characteristic that you can only use non-targeted APIs like `trigger` with a `GlobalEvent` and targeted APIs like `trigger_targets` with a `TargetedEvent`. You have full control over whether the entity should or should not have a target, as they are fully distinct at the type-level. However, there's a few problems: - There is no type-level indication of whether a `GlobalEvent` supports buffered events or just non-targeted observer events - An `Event` on its own does literally nothing, it's just a shared trait required to make global observers accept both non-targeted and targeted events - If an event is both a `GlobalEvent` and `TargetedEvent`, global observers again have ambiguity on whether an event has a target or not, undermining some of the benefits - The names are not ideal ### 4. `Event` and `EntityEvent` We can fix some of the problems of Alternative 3 by accepting that targeted events can also be used in non-targeted contexts, and simply having the `Event` and `EntityEvent` traits: ```rust // For buffered events and non-targeted observer events pub trait Event: Send + Sync + 'static { fn register_component_id(world: &mut World) -> ComponentId { ... } fn component_id(world: &World) -> Option<ComponentId> { ... } } // For targeted observer events pub trait EntityEvent: Event { type Traversal: Traversal<Self>; const AUTO_PROPAGATE: bool = false; } ``` This is essentially identical to this PR, just without a dedicated `BufferedEvent`. The remaining major "problem" is that there is still zero type-level indication of whether an `Event` event *actually* supports the buffered API. This leads us to the solution proposed in this PR, using `Event`, `EntityEvent`, and `BufferedEvent`. </details> ## Conclusion The `Event` + `EntityEvent` + `BufferedEvent` split proposed in this PR aims to solve all the common problems with Bevy's current event model while keeping the "weirdness" factor minimal. It splits in terms of both the push vs. pull *and* global vs. targeted aspects, while maintaining a shared concept for an "event". ### Why I Like This - The term "event" remains as a single concept for all the different kinds of events in Bevy. - Despite all event types being "events", they use fundamentally different APIs. Instead of assuming that you can use an event type with any pattern (when only one is typically supported), you explicitly opt in to each one with dedicated traits. - Using separate traits for each type of event helps with documentation and clearer function signatures. - I can safely make assumptions on expected usage. - If I see that an event is an `EntityEvent`, I can assume that I can use `observe` on it and get targeted events. - If I see that an event is a `BufferedEvent`, I can assume that I can use `EventReader` to read events. - If I see both `EntityEvent` and `BufferedEvent`, I can assume that both APIs are supported. In summary: This allows for a unified concept for events, while limiting the different ways to use them with opt-in traits. No more guess-work involved when using APIs. ### Problems? - Because `BufferedEvent` implements `Event` (for more consistent semantics etc.), you can still use all buffered events for non-targeted observers. I think this is fine/good. The important part is that if you see that an event implements `BufferedEvent`, you know that the `EventReader`/`EventWriter` API should be supported. Whether it *also* supports other APIs is secondary. - I currently only support `trigger_targets` for an `EntityEvent`. However, you can technically target components too, without targeting any entities. I consider that such a niche and advanced use case that it's not a huge problem to only support it for `EntityEvent`s, but we could also split `trigger_targets` into `trigger_entities` and `trigger_components` if we wanted to (or implement components as entities :P). - You can still trigger an `EntityEvent` *without* targets. I consider this correct, since `Event` implements the non-targeted behavior, and it'd be weird if implementing another trait *removed* behavior. However, it does mean that global observers for entity events can technically return `Entity::PLACEHOLDER` again (since I got rid of the `Option<Entity>` added in #19440 for ergonomics). I think that's enough of an edge case that it's not a huge problem, but it is worth keeping in mind. - ~~Deriving both `EntityEvent` and `BufferedEvent` for the same type currently duplicates the `Event` implementation, so you instead need to manually implement one of them.~~ Changed to always requiring `Event` to be derived. ## Related Work There are plans to implement multi-event support for observers, especially for UI contexts. [Cart's example](https://github.com/bevyengine/bevy/issues/14649#issuecomment-2960402508) API looked like this: ```rust // Truncated for brevity trigger: Trigger<( OnAdd<Pressed>, OnRemove<Pressed>, OnAdd<InteractionDisabled>, OnRemove<InteractionDisabled>, OnInsert<Hovered>, )>, ``` I believe this shouldn't be in conflict with this PR. If anything, this PR might *help* achieve the multi-event pattern for entity observers with fewer footguns: by statically enforcing that all of these events are `EntityEvent`s in the context of `EntityCommands::observe`, we can avoid misuse or weird cases where *some* events inside the trigger are targeted while others are not. |
||
|
|
e9a0ef49f9 |
Rename bevy_platform_support to bevy_platform (#18813)
# Objective The goal of `bevy_platform_support` is to provide a set of platform agnostic APIs, alongside platform-specific functionality. This is a high traffic crate (providing things like HashMap and Instant). Especially in light of https://github.com/bevyengine/bevy/discussions/18799, it deserves a friendlier / shorter name. Given that it hasn't had a full release yet, getting this change in before Bevy 0.16 makes sense. ## Solution - Rename `bevy_platform_support` to `bevy_platform`. |
||
|
|
c3ff6d4136 |
Fix non-crate typos (#18219)
# Objective Correct spelling ## Solution Fix typos, specifically ones that I found in folders other than /crates ## Testing CI --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
bfe932d1f3 |
Rework WindowMode::Fullscreen API (#17525)
# Objective - #16883 - Improve the default behaviour of the exclusive fullscreen API. ## Solution This PR changes the exclusive fullscreen window mode to require the type `WindowMode::Fullscreen(MonitorSelection, VideoModeSelection)` and removes `WindowMode::SizedFullscreen`. This API somewhat intentionally more closely resembles Winit 0.31's upcoming fullscreen and video mode API. The new VideoModeSelection enum is specified as follows: ```rust pub enum VideoModeSelection { /// Uses the video mode that the monitor is already in. Current, /// Uses a given [`crate::monitor::VideoMode`]. A list of video modes supported by the monitor /// is supplied by [`crate::monitor::Monitor::video_modes`]. Specific(VideoMode), } ``` ### Changing default behaviour This might be contentious because it removes the previous behaviour of `WindowMode::Fullscreen` which selected the highest resolution possible. While the previous behaviour would be quite easy to re-implement as additional options, or as an impl method on Monitor, I would argue that this isn't an implementation that should be encouraged. From the perspective of a Windows user, I prefer what the majority of modern games do when entering fullscreen which is to preserve the OS's current resolution settings, which allows exclusive fullscreen to be entered faster, and to only have it change if I manually select it in either the options of the game or the OS. The highest resolution available is not necessarily what the user prefers. I am open to changing this if I have just missed a good use case for it. Likewise, the only functionality that `WindowMode::SizedFullscreen` provided was that it selected the resolution closest to the current size of the window so it was removed since this behaviour can be replicated via the new `VideoModeSelection::Specific` if necessary. ## Out of scope WindowResolution and scale factor act strangely in exclusive fullscreen, this PR doesn't address it or regress it. ## Testing - Tested on Windows 11 and macOS 12.7 - Linux untested ## Migration Guide `WindowMode::SizedFullscreen(MonitorSelection)` and `WindowMode::Fullscreen(MonitorSelection)` has become `WindowMode::Fullscreen(MonitorSelection, VideoModeSelection)`. Previously, the VideoMode was selected based on the closest resolution to the current window size for SizedFullscreen and the largest resolution for Fullscreen. It is possible to replicate that behaviour by searching `Monitor::video_modes` and selecting it with `VideoModeSelection::Specific(VideoMode)` but it is recommended to use `VideoModeSelection::Current` as the default video mode when entering fullscreen. |
||
|
|
2ad5908e58 |
Make Query::single (and friends) return a Result (#18082)
# Objective As discussed in #14275, Bevy is currently too prone to panic, and makes the easy / beginner-friendly way to do a large number of operations just to panic on failure. This is seriously frustrating in library code, but also slows down development, as many of the `Query::single` panics can actually safely be an early return (these panics are often due to a small ordering issue or a change in game state. More critically, in most "finished" products, panics are unacceptable: any unexpected failures should be handled elsewhere. That's where the new With the advent of good system error handling, we can now remove this. Note: I was instrumental in a) introducing this idea in the first place and b) pushing to make the panicking variant the default. The introduction of both `let else` statements in Rust and the fancy system error handling work in 0.16 have changed my mind on the right balance here. ## Solution 1. Make `Query::single` and `Query::single_mut` (and other random related methods) return a `Result`. 2. Handle all of Bevy's internal usage of these APIs. 3. Deprecate `Query::get_single` and friends, since we've moved their functionality to the nice names. 4. Add detailed advice on how to best handle these errors. Generally I like the diff here, although `get_single().unwrap()` in tests is a bit of a downgrade. ## Testing I've done a global search for `.single` to track down any missed deprecated usages. As to whether or not all the migrations were successful, that's what CI is for :) ## Future work ~~Rename `Query::get_single` and friends to `Query::single`!~~ ~~I've opted not to do this in this PR, and smear it across two releases in order to ease the migration. Successive deprecations are much easier to manage than the semantics and types shifting under your feet.~~ Cart has convinced me to change my mind on this; see https://github.com/bevyengine/bevy/pull/18082#discussion_r1974536085. ## Migration guide `Query::single`, `Query::single_mut` and their `QueryState` equivalents now return a `Result`. Generally, you'll want to: 1. Use Bevy 0.16's system error handling to return a `Result` using the `?` operator. 2. Use a `let else Ok(data)` block to early return if it's an expected failure. 3. Use `unwrap()` or `Ok` destructuring inside of tests. The old `Query::get_single` (etc) methods which did this have been deprecated. |
||
|
|
5f86668bbb |
Renamed EventWriter::send methods to write. (#17977)
Fixes #17856. ## Migration Guide - `EventWriter::send` has been renamed to `EventWriter::write`. - `EventWriter::send_batch` has been renamed to `EventWriter::write_batch`. - `EventWriter::send_default` has been renamed to `EventWriter::write_default`. --------- Co-authored-by: François Mockers <mockersf@gmail.com> |
||
|
|
9bc0ae33c3 |
Move hashbrown and foldhash out of bevy_utils (#17460)
# Objective - Contributes to #16877 ## Solution - Moved `hashbrown`, `foldhash`, and related types out of `bevy_utils` and into `bevy_platform_support` - Refactored the above to match the layout of these types in `std`. - Updated crates as required. ## Testing - CI --- ## Migration Guide - The following items were moved out of `bevy_utils` and into `bevy_platform_support::hash`: - `FixedState` - `DefaultHasher` - `RandomState` - `FixedHasher` - `Hashed` - `PassHash` - `PassHasher` - `NoOpHash` - The following items were moved out of `bevy_utils` and into `bevy_platform_support::collections`: - `HashMap` - `HashSet` - `bevy_utils::hashbrown` has been removed. Instead, import from `bevy_platform_support::collections` _or_ take a dependency on `hashbrown` directly. - `bevy_utils::Entry` has been removed. Instead, import from `bevy_platform_support::collections::hash_map` or `bevy_platform_support::collections::hash_set` as appropriate. - All of the above equally apply to `bevy::utils` and `bevy::platform_support`. ## Notes - I left `PreHashMap`, `PreHashMapExt`, and `TypeIdMap` in `bevy_utils` as they might be candidates for micro-crating. They can always be moved into `bevy_platform_support` at a later date if desired. |
||
|
|
21f1e3045c |
Relationships (non-fragmenting, one-to-many) (#17398)
This adds support for one-to-many non-fragmenting relationships (with planned paths for fragmenting and non-fragmenting many-to-many relationships). "Non-fragmenting" means that entities with the same relationship type, but different relationship targets, are not forced into separate tables (which would cause "table fragmentation"). Functionally, this fills a similar niche as the current Parent/Children system. The biggest differences are: 1. Relationships have simpler internals and significantly improved performance and UX. Commands and specialized APIs are no longer necessary to keep everything in sync. Just spawn entities with the relationship components you want and everything "just works". 2. Relationships are generalized. Bevy can provide additional built in relationships, and users can define their own. **REQUEST TO REVIEWERS**: _please don't leave top level comments and instead comment on specific lines of code. That way we can take advantage of threaded discussions. Also dont leave comments simply pointing out CI failures as I can read those just fine._ ## Built on top of what we have Relationships are implemented on top of the Bevy ECS features we already have: components, immutability, and hooks. This makes them immediately compatible with all of our existing (and future) APIs for querying, spawning, removing, scenes, reflection, etc. The fewer specialized APIs we need to build, maintain, and teach, the better. ## Why focus on one-to-many non-fragmenting first? 1. This allows us to improve Parent/Children relationships immediately, in a way that is reasonably uncontroversial. Switching our hierarchy to fragmenting relationships would have significant performance implications. ~~Flecs is heavily considering a switch to non-fragmenting relations after careful considerations of the performance tradeoffs.~~ _(Correction from @SanderMertens: Flecs is implementing non-fragmenting storage specialized for asset hierarchies, where asset hierarchies are many instances of small trees that have a well defined structure)_ 2. Adding generalized one-to-many relationships is currently a priority for the [Next Generation Scene / UI effort](https://github.com/bevyengine/bevy/discussions/14437). Specifically, we're interested in building reactions and observers on top. ## The changes This PR does the following: 1. Adds a generic one-to-many Relationship system 3. Ports the existing Parent/Children system to Relationships, which now lives in `bevy_ecs::hierarchy`. The old `bevy_hierarchy` crate has been removed. 4. Adds on_despawn component hooks 5. Relationships can opt-in to "despawn descendants" behavior, meaning that the entire relationship hierarchy is despawned when `entity.despawn()` is called. The built in Parent/Children hierarchies enable this behavior, and `entity.despawn_recursive()` has been removed. 6. `world.spawn` now applies commands after spawning. This ensures that relationship bookkeeping happens immediately and removes the need to manually flush. This is in line with the equivalent behaviors recently added to the other APIs (ex: insert). 7. Removes the ValidParentCheckPlugin (system-driven / poll based) in favor of a `validate_parent_has_component` hook. ## Using Relationships The `Relationship` trait looks like this: ```rust pub trait Relationship: Component + Sized { type RelationshipSources: RelationshipSources<Relationship = Self>; fn get(&self) -> Entity; fn from(entity: Entity) -> Self; } ``` A relationship is a component that: 1. Is a simple wrapper over a "target" Entity. 2. Has a corresponding `RelationshipSources` component, which is a simple wrapper over a collection of entities. Every "target entity" targeted by a "source entity" with a `Relationship` has a `RelationshipSources` component, which contains every "source entity" that targets it. For example, the `Parent` component (as it currently exists in Bevy) is the `Relationship` component and the entity containing the Parent is the "source entity". The entity _inside_ the `Parent(Entity)` component is the "target entity". And that target entity has a `Children` component (which implements `RelationshipSources`). In practice, the Parent/Children relationship looks like this: ```rust #[derive(Relationship)] #[relationship(relationship_sources = Children)] pub struct Parent(pub Entity); #[derive(RelationshipSources)] #[relationship_sources(relationship = Parent)] pub struct Children(Vec<Entity>); ``` The Relationship and RelationshipSources derives automatically implement Component with the relevant configuration (namely, the hooks necessary to keep everything in sync). The most direct way to add relationships is to spawn entities with relationship components: ```rust let a = world.spawn_empty().id(); let b = world.spawn(Parent(a)).id(); assert_eq!(world.entity(a).get::<Children>().unwrap(), &[b]); ``` There are also convenience APIs for spawning more than one entity with the same relationship: ```rust world.spawn_empty().with_related::<Children>(|s| { s.spawn_empty(); s.spawn_empty(); }) ``` The existing `with_children` API is now a simpler wrapper over `with_related`. This makes this change largely non-breaking for existing spawn patterns. ```rust world.spawn_empty().with_children(|s| { s.spawn_empty(); s.spawn_empty(); }) ``` There are also other relationship APIs, such as `add_related` and `despawn_related`. ## Automatic recursive despawn via the new on_despawn hook `RelationshipSources` can opt-in to "despawn descendants" behavior, which will despawn all related entities in the relationship hierarchy: ```rust #[derive(RelationshipSources)] #[relationship_sources(relationship = Parent, despawn_descendants)] pub struct Children(Vec<Entity>); ``` This means that `entity.despawn_recursive()` is no longer required. Instead, just use `entity.despawn()` and the relevant related entities will also be despawned. To despawn an entity _without_ despawning its parent/child descendants, you should remove the `Children` component first, which will also remove the related `Parent` components: ```rust entity .remove::<Children>() .despawn() ``` This builds on the on_despawn hook introduced in this PR, which is fired when an entity is despawned (before other hooks). ## Relationships are the source of truth `Relationship` is the _single_ source of truth component. `RelationshipSources` is merely a reflection of what all the `Relationship` components say. By embracing this, we are able to significantly improve the performance of the system as a whole. We can rely on component lifecycles to protect us against duplicates, rather than needing to scan at runtime to ensure entities don't already exist (which results in quadratic runtime). A single source of truth gives us constant-time inserts. This does mean that we cannot directly spawn populated `Children` components (or directly add or remove entities from those components). I personally think this is a worthwhile tradeoff, both because it makes the performance much better _and_ because it means theres exactly one way to do things (which is a philosophy we try to employ for Bevy APIs). As an aside: treating both sides of the relationship as "equivalent source of truth relations" does enable building simple and flexible many-to-many relationships. But this introduces an _inherent_ need to scan (or hash) to protect against duplicates. [`evergreen_relations`](https://github.com/EvergreenNest/evergreen_relations) has a very nice implementation of the "symmetrical many-to-many" approach. Unfortunately I think the performance issues inherent to that approach make it a poor choice for Bevy's default relationship system. ## Followup Work * Discuss renaming `Parent` to `ChildOf`. I refrained from doing that in this PR to keep the diff reasonable, but I'm personally biased toward this change (and using that naming pattern generally for relationships). * [Improved spawning ergonomics](https://github.com/bevyengine/bevy/discussions/16920) * Consider adding relationship observers/triggers for "relationship targets" whenever a source is added or removed. This would replace the current "hierarchy events" system, which is unused upstream but may have existing users downstream. I think triggers are the better fit for this than a buffered event queue, and would prefer not to add that back. * Fragmenting relations: My current idea hinges on the introduction of "value components" (aka: components whose type _and_ value determines their ComponentId, via something like Hashing / PartialEq). By labeling a Relationship component such as `ChildOf(Entity)` as a "value component", `ChildOf(e1)` and `ChildOf(e2)` would be considered "different components". This makes the transition between fragmenting and non-fragmenting a single flag, and everything else continues to work as expected. * Many-to-many support * Non-fragmenting: We can expand Relationship to be a list of entities instead of a single entity. I have largely already written the code for this. * Fragmenting: With the "value component" impl mentioned above, we get many-to-many support "for free", as it would allow inserting multiple copies of a Relationship component with different target entities. Fixes #3742 (If this PR is merged, I think we should open more targeted followup issues for the work above, with a fresh tracking issue free of the large amount of less-directed historical context) Fixes #17301 Fixes #12235 Fixes #15299 Fixes #15308 ## Migration Guide * Replace `ChildBuilder` with `ChildSpawnerCommands`. * Replace calls to `.set_parent(parent_id)` with `.insert(Parent(parent_id))`. * Replace calls to `.replace_children()` with `.remove::<Children>()` followed by `.add_children()`. Note that you'll need to manually despawn any children that are not carried over. * Replace calls to `.despawn_recursive()` with `.despawn()`. * Replace calls to `.despawn_descendants()` with `.despawn_related::<Children>()`. * If you have any calls to `.despawn()` which depend on the children being preserved, you'll need to remove the `Children` component first. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
21786632c3 |
Remove bevy_core (#16897)
# Objective - Fixes #16892 ## Solution - Removed `TypeRegistryPlugin` (`Name` is now automatically registered with a default `App`) - Moved `TaskPoolPlugin` to `bevy_app` - Moved `FrameCountPlugin` to `bevy_diagnostic` - Deleted now-empty `bevy_core` ## Testing - CI ## Migration Guide - `TypeRegistryPlugin` no longer exists. If you can't use a default `App` but still need `Name` registered, do so manually with `app.register_type::<Name>()`. - References to `TaskPoolPlugin` and associated types will need to import it from `bevy_app` instead of `bevy_core` - References to `FrameCountPlugin` and associated types will need to import it from `bevy_diagnostic` instead of `bevy_core` ## Notes This strategy was agreed upon by Cart and several other members in [Discord](https://discord.com/channels/691052431525675048/692572690833473578/1319137218312278077). |
||
|
|
f2719f5470 |
Rust 1.83, allow -> expect (missing_docs) (#16561)
# Objective We were waiting for 1.83 to address most of these, due to a bug with `missing_docs` and `expect`. Relates to, but does not entirely complete, #15059. ## Solution - Upgrade to 1.83 - Switch `allow(missing_docs)` to `expect(missing_docs)` - Remove a few now-unused `allow`s along the way, or convert to `expect` |
||
|
|
711246aa34 |
Update hashbrown to 0.15 (#15801)
Updating dependencies; adopted version of #15696. (Supercedes #15696.) Long answer: hashbrown is no longer using ahash by default, meaning that we can't use the default-hasher methods with ahasher. So, we have to use the longer-winded versions instead. This takes the opportunity to also switch our default hasher as well, but without actually enabling the default-hasher feature for hashbrown, meaning that we'll be able to change our hasher more easily at the cost of all of these method calls being obnoxious forever. One large change from 0.15 is that `insert_unique_unchecked` is now `unsafe`, and for cases where unsafe code was denied at the crate level, I replaced it with `insert`. ## Migration Guide `bevy_utils` has updated its version of `hashbrown` to 0.15 and now defaults to `foldhash` instead of `ahash`. This means that if you've hard-coded your hasher to `bevy_utils::AHasher` or separately used the `ahash` crate in your code, you may need to switch to `foldhash` to ensure that everything works like it does in Bevy. |
||
|
|
bb81a2cdb3 |
Simple integration testing (adopted) (#16489)
# Objective This older PR from `Wcubed` seemed well worth saving, adopted from #7314. See also tracking issue #2896 for ongoing discussion of Bevy testability. Thanks `Wcubed`! ## Solution - Updated for 0.15 - Added the `expected`/`actual` pattern - Switched to function plugin - Tweaked a bit of description ## Testing Green. --------- Co-authored-by: Wybe Westra <dev@wwestra.nl> Co-authored-by: Wybe Westra <wybe.westra@protonmail.com> |
||
|
|
30d84519a2 |
Use en-us locale for typos (#16037)
# Objective Bevy seems to want to standardize on "American English" spellings. Not sure if this is laid out anywhere in writing, but see also #15947. While perusing the docs for `typos`, I noticed that it has a `locale` config option and tried it out. ## Solution Switch to `en-us` locale in the `typos` config and run `typos -w` ## Migration Guide The following methods or fields have been renamed from `*dependants*` to `*dependents*`. - `ProcessorAssetInfo::dependants` - `ProcessorAssetInfos::add_dependant` - `ProcessorAssetInfos::non_existent_dependants` - `AssetInfo::dependants_waiting_on_load` - `AssetInfo::dependants_waiting_on_recursive_dep_load` - `AssetInfos::loader_dependants` - `AssetInfos::remove_dependants_and_labels` |
||
|
|
d96a9d15f6 |
Migrate from Query::single and friends to Single (#15872)
# Objective - closes #15866 ## Solution - Simply migrate where possible. ## Testing - Expect that CI will do most of the work. Examples is another way of testing this, as most of the work is in that area. --- ## Notes For now, this PR doesn't migrate `QueryState::single` and friends as for now, this look like another issue. So for example, QueryBuilders that used single or `World::query` that used single wasn't migrated. If there is a easy way to migrate those, please let me know. Most of the uses of `Query::single` were removed, the only other uses that I found was related to tests of said methods, so will probably be removed when we remove `Query::single`. |
||
|
|
7f24c27645 |
Fix minimising example minimizing every frame (#15850)
# Objective The `minimising` example is a bit annoying to run locally, because it attempts to minimize the window every frame, so un-minimizing it is difficult. ## Solution Only minimize once. The contents of the example can now be inspected, and the window easily closed after the minimization happens. ## Testing `cargo run --example minimising` I tested on macos only. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
|
|
7d40e3ec87 |
Migrate bevy_sprite to required components (#15489)
# Objective Continue migration of bevy APIs to required components, following guidance of https://hackmd.io/@bevy/required_components/ ## Solution - Make `Sprite` require `Transform` and `Visibility` and `SyncToRenderWorld` - move image and texture atlas handles into `Sprite` - deprecate `SpriteBundle` - remove engine uses of `SpriteBundle` ## Testing ran cargo tests on bevy_sprite and tested several sprite examples. --- ## Migration Guide Replace all uses of `SpriteBundle` with `Sprite`. There are several new convenience constructors: `Sprite::from_image`, `Sprite::from_atlas_image`, `Sprite::from_color`. WARNING: use of `Handle<Image>` and `TextureAtlas` as components on sprite entities will NO LONGER WORK. Use the fields on `Sprite` instead. I would have removed the `Component` impls from `TextureAtlas` and `Handle<Image>` except it is still used within ui. We should fix this moving forward with the migration. |
||
|
|
25bfa80e60 |
Migrate cameras to required components (#15641)
# Objective Yet another PR for migrating stuff to required components. This time, cameras! ## Solution As per the [selected proposal](https://hackmd.io/tsYID4CGRiWxzsgawzxG_g#Combined-Proposal-1-Selected), deprecate `Camera2dBundle` and `Camera3dBundle` in favor of `Camera2d` and `Camera3d`. Adding a `Camera` without `Camera2d` or `Camera3d` now logs a warning, as suggested by Cart [on Discord](https://discord.com/channels/691052431525675048/1264881140007702558/1291506402832945273). I would personally like cameras to work a bit differently and be split into a few more components, to avoid some footguns and confusing semantics, but that is more controversial, and shouldn't block this core migration. ## Testing I ran a few 2D and 3D examples, and tried cameras with and without render graphs. --- ## Migration Guide `Camera2dBundle` and `Camera3dBundle` have been deprecated in favor of `Camera2d` and `Camera3d`. Inserting them will now also insert the other components required by them automatically. |
||
|
|
54006b107b |
Migrate meshes and materials to required components (#15524)
# Objective A big step in the migration to required components: meshes and materials! ## Solution As per the [selected proposal](https://hackmd.io/@bevy/required_components/%2Fj9-PnF-2QKK0on1KQ29UWQ): - Deprecate `MaterialMesh2dBundle`, `MaterialMeshBundle`, and `PbrBundle`. - Add `Mesh2d` and `Mesh3d` components, which wrap a `Handle<Mesh>`. - Add `MeshMaterial2d<M: Material2d>` and `MeshMaterial3d<M: Material>`, which wrap a `Handle<M>`. - Meshes *without* a mesh material should be rendered with a default material. The existence of a material is determined by `HasMaterial2d`/`HasMaterial3d`, which is required by `MeshMaterial2d`/`MeshMaterial3d`. This gets around problems with the generics. Previously: ```rust commands.spawn(MaterialMesh2dBundle { mesh: meshes.add(Circle::new(100.0)).into(), material: materials.add(Color::srgb(7.5, 0.0, 7.5)), transform: Transform::from_translation(Vec3::new(-200., 0., 0.)), ..default() }); ``` Now: ```rust commands.spawn(( Mesh2d(meshes.add(Circle::new(100.0))), MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))), Transform::from_translation(Vec3::new(-200., 0., 0.)), )); ``` If the mesh material is missing, previously nothing was rendered. Now, it renders a white default `ColorMaterial` in 2D and a `StandardMaterial` in 3D (this can be overridden). Below, only every other entity has a material:   Why white? This is still open for discussion, but I think white makes sense for a *default* material, while *invalid* asset handles pointing to nothing should have something like a pink material to indicate that something is broken (I don't handle that in this PR yet). This is kind of a mix of Godot and Unity: Godot just renders a white material for non-existent materials, while Unity renders nothing when no materials exist, but renders pink for invalid materials. I can also change the default material to pink if that is preferable though. ## Testing I ran some 2D and 3D examples to test if anything changed visually. I have not tested all examples or features yet however. If anyone wants to test more extensively, it would be appreciated! ## Implementation Notes - The relationship between `bevy_render` and `bevy_pbr` is weird here. `bevy_render` needs `Mesh3d` for its own systems, but `bevy_pbr` has all of the material logic, and `bevy_render` doesn't depend on it. I feel like the two crates should be refactored in some way, but I think that's out of scope for this PR. - I didn't migrate meshlets to required components yet. That can probably be done in a follow-up, as this is already a huge PR. - It is becoming increasingly clear to me that we really, *really* want to disallow raw asset handles as components. They caused me a *ton* of headache here already, and it took me a long time to find every place that queried for them or inserted them directly on entities, since there were no compiler errors for it. If we don't remove the `Component` derive, I expect raw asset handles to be a *huge* footgun for users as we transition to wrapper components, especially as handles as components have been the norm so far. I personally consider this to be a blocker for 0.15: we need to migrate to wrapper components for asset handles everywhere, and remove the `Component` derive. Also see https://github.com/bevyengine/bevy/issues/14124. --- ## Migration Guide Asset handles for meshes and mesh materials must now be wrapped in the `Mesh2d` and `MeshMaterial2d` or `Mesh3d` and `MeshMaterial3d` components for 2D and 3D respectively. Raw handles as components no longer render meshes. Additionally, `MaterialMesh2dBundle`, `MaterialMeshBundle`, and `PbrBundle` have been deprecated. Instead, use the mesh and material components directly. Previously: ```rust commands.spawn(MaterialMesh2dBundle { mesh: meshes.add(Circle::new(100.0)).into(), material: materials.add(Color::srgb(7.5, 0.0, 7.5)), transform: Transform::from_translation(Vec3::new(-200., 0., 0.)), ..default() }); ``` Now: ```rust commands.spawn(( Mesh2d(meshes.add(Circle::new(100.0))), MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))), Transform::from_translation(Vec3::new(-200., 0., 0.)), )); ``` If the mesh material is missing, a white default material is now used. Previously, nothing was rendered if the material was missing. The `WithMesh2d` and `WithMesh3d` query filter type aliases have also been removed. Simply use `With<Mesh2d>` or `With<Mesh3d>`. --------- Co-authored-by: Tim Blackbird <justthecooldude@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
|
|
de888a373d |
Migrate lights to required components (#15554)
# Objective Another step in the migration to required components: lights! Note that this does not include `EnvironmentMapLight` or reflection probes yet, because their API hasn't been fully chosen yet. ## Solution As per the [selected proposals](https://hackmd.io/@bevy/required_components/%2FLLnzwz9XTxiD7i2jiUXkJg): - Deprecate `PointLightBundle` in favor of the `PointLight` component - Deprecate `SpotLightBundle` in favor of the `PointLight` component - Deprecate `DirectionalLightBundle` in favor of the `DirectionalLight` component ## Testing I ran some examples with lights. --- ## Migration Guide `PointLightBundle`, `SpotLightBundle`, and `DirectionalLightBundle` have been deprecated. Use the `PointLight`, `SpotLight`, and `DirectionalLight` components instead. Adding them will now insert the other components required by them automatically. |
||
|
|
efda7f3f9c |
Simpler lint fixes: makes ci lints work but disables a lint for now (#15376)
Takes the first two commits from #15375 and adds suggestions from this comment: https://github.com/bevyengine/bevy/pull/15375#issuecomment-2366968300 See #15375 for more reasoning/motivation. ## Rebasing (rerunning) ```rust git switch simpler-lint-fixes git reset --hard main cargo fmt --all -- --unstable-features --config normalize_comments=true,imports_granularity=Crate cargo fmt --all git add --update git commit --message "rustfmt" cargo clippy --workspace --all-targets --all-features --fix cargo fmt --all -- --unstable-features --config normalize_comments=true,imports_granularity=Crate cargo fmt --all git add --update git commit --message "clippy" git cherry-pick e6c0b94f6795222310fb812fa5c4512661fc7887 ``` |
||
|
|
4742f74fc4 |
Broaden "Check for bevy_internal imports" CI Task (#15333)
# Objective - Fixes #15319 - Fixes #15317 ## Solution - Updated CI task to check for _any_ `bevy_*` crates, rather than just `bevy_internal` --------- Co-authored-by: François Mockers <francois.mockers@vleue.com> |
||
|
|
ec728c31c1 |
style: simplify string formatting for readability (#15033)
# Objective The goal of this change is to improve code readability and maintainability. |
||
|
|
4ac2a63556 |
Remove all existing system order ambiguities in DefaultPlugins (#15031)
# Objective As discussed in https://github.com/bevyengine/bevy/issues/7386, system order ambiguities within `DefaultPlugins` are a source of bugs in the engine and badly pollute diagnostic output for users. We should eliminate them! This PR is an alternative to #15027: with all external ambiguities silenced, this should be much less prone to merge conflicts and the test output should be much easier for authors to understand. Note that system order ambiguities are still permitted in the `RenderApp`: these need a bit of thought in terms of how to test them, and will be fairly involved to fix. While these aren't *good*, they'll generally only cause graphical bugs, not logic ones. ## Solution All remaining system order ambiguities have been resolved. Review this PR commit-by-commit to see how each of these problems were fixed. ## Testing `cargo run --example ambiguity_detection` passes with no panics or logging! |
||
|
|
01a3b0e830 |
UI texture atlas slice shader (#14990)
# Objective Fixes https://github.com/bevyengine/bevy/issues/14183 ## Solution Reimplement the UI texture atlas slicer using a shader. The problems with #14183 could be fixed more simply by hacking around with the coordinates and scaling but that way is very fragile and might get broken again the next time we make changes to the layout calculations. A shader based solution is more robust, it's impossible for gaps to appear between the image slices with these changes as we're only drawing a single quad. I've not tried any benchmarks yet but it should much more efficient as well, in the worst cases even hundreds or thousands of times faster. Maybe could have used the UiMaterialPipeline. I wrote the shader first and used fat vertices and then realised it wouldn't work that way with a UiMaterial. If it's rewritten it so it puts all the slice geometry in uniform buffer, then it might work? Adding the uniform buffer would probably make the shader more complicated though, so don't know if it's even worth it. Instancing is another alternative. ## Testing The examples are working and it seems to match the old API correctly but I've not used the texture atlas slicing API for anything before, I reviewed the PR but that was back in January. Needs a review by someone who knows the rendering pipeline and wgsl really well because I don't really have any idea what I'm doing. |
||
|
|
be100b8760 |
Resolve UI outlines using the correct target's viewport size (#14947)
# Objective `resolve_outlines_system` wasn't updated when multi-window support was added and it always uses the size of the primary window when resolving viewport coords, regardless of the layout's camera target. Fixes #14945 ## Solution It's awkward to get the viewport size of the target for an individual node without walking the tree or adding extra fields to `Node`, so I removed `resolve_outlines_system` and instead the outline values are updated in `ui_layout_system`. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
|
|
5243fe6956 |
Remove manual apply_deferred in bevy_ui (#14768)
# Objective This `apply_deferred` doesn't seem to have any effect, pointlessly restricts parallelism and is responsible for a large number of system order ambiguities. Spotted as part of #7386. ## Solution Remove it. This is the *only* manual apply_deferred in the code base currently. ## Testing I've checked various UI examples and `split_screen`, and couldn't discern any difference. This looks like a remnant of a `(a, apply_deferred, b).chain()` pattern where `b` got removed, leaving us with a weird vestige. |
||
|
|
0c126ee676 |
Improve ambiguity detection example / test (#14760)
# Objective While tackling https://github.com/bevyengine/bevy/issues/7386, I noticed a few nits / frustrations with the existing testing infrastructure. Rather than mixing those changes in with much more challenging to review changes to reduce ambiguities, I've split this work into its own PR. ## Solution Substantially simplify the `ambiguity_detection` test code, and reduce the verbosity of logging. ## Testing When run locally, the test functions as expected, with somewhat cleaner logging. --------- Co-authored-by: Jan Hohenheim <jan@hohenheim.ch> |
||
|
|
9b254aab1e |
Explicitly order CameraUpdateSystem before UiSystem::Prepare (#14609)
# Objective Fixes https://github.com/bevyengine/bevy/issues/14277. May also fix https://github.com/bevyengine/bevy/issues/14255, needs verification. ## Solution Explicitly order `CameraUpdateSystem` before `UiSystem::Prepare`, so that when the window resizes, `camera_system` will update the `Camera`'s viewport size before `ui_layout_system` also reacts to the window resize and tries to read the new `Camera` viewport size to set UI node sizes accordingly. ## Testing I tested that explicitly ordering `CameraUpdateSystem` _after_ triggers the buggy behavior, and explicitly ordering it _before_ does not trigger the buggy behavior or crash the app (which also demonstrates that the system sets are ambiguous). --- ## Migration Guide `CameraUpdateSystem` is now explicitly ordered before `UiSystem::Prepare` instead of being ambiguous with it. |
||
|
|
e579622a65 |
time_system is ambiguous_with event_update_system (#14544)
# Objective Resolve possible ambiguity detection panic between `time_system` and `event_update_system`. Fixes #14524 ## Solution Sets `.ambiguous_with(event_update_system)` on `time_system`. This is slightly new territory for me, so please treat with scepticism. ## Testing As described in the issue, added ``` .configure_schedules(ScheduleBuildSettings { ambiguity_detection: LogLevel::Error, ..default() }) ``` to the `time` example and ran it. |
||
|
|
26fc4c7198 |
Test for ambiguous system ordering in CI (#13950)
Progress towards https://github.com/bevyengine/bevy/issues/7386. Following discussion https://discord.com/channels/691052431525675048/1253260494538539048/1253387942311886960 This Pull Request adds an example to detect system order ambiguities, and also asserts none exist. A lot of schedules are ignored in ordered to have the test passing, we should thrive to make them pass, but in other pull requests. <details><summary>example output <b>summary</b>, without ignored schedules</summary> <p> ```txt $ cargo run --example ambiguity_detection 2>&1 | grep -C 1 "pairs of syst" 2024-06-21T13:17:55.776585Z WARN bevy_ecs::schedule::schedule: Schedule First has ambiguities. 1 pairs of systems with conflicting data access have indeterminate execution order. Consider adding `before`, `after`, or `ambiguous_with` relationships between these: -- bevy_time::time_system (in set TimeSystem) and bevy_ecs::event::event_update_system (in set EventUpdates) -- 2024-06-21T13:17:55.782265Z WARN bevy_ecs::schedule::schedule: Schedule PreUpdate has ambiguities. 11 pairs of systems with conflicting data access have indeterminate execution order. Consider adding `before`, `after`, or `ambiguous_with` relationships between these: -- bevy_pbr::prepass::update_mesh_previous_global_transforms and bevy_asset::server::handle_internal_asset_events -- 2024-06-21T13:17:55.809516Z WARN bevy_ecs::schedule::schedule: Schedule PostUpdate has ambiguities. 63 pairs of systems with conflicting data access have indeterminate execution order. Consider adding `before`, `after`, or `ambiguous_with` relationships between these: -- bevy_ui::accessibility::image_changed and bevy_ecs::schedule::executor::apply_deferred -- 2024-06-21T13:17:55.816287Z WARN bevy_ecs::schedule::schedule: Schedule Last has ambiguities. 3 pairs of systems with conflicting data access have indeterminate execution order. Consider adding `before`, `after`, or `ambiguous_with` relationships between these: -- bevy_gizmos::update_gizmo_meshes<bevy_gizmos::aabb::AabbGizmoConfigGroup> (in set UpdateGizmoMeshes) and bevy_gizmos::update_gizmo_meshes<bevy_gizmos::light::LightGizmoConfigGroup> (in set UpdateGizmoMeshes) -- 2024-06-21T13:17:55.831074Z WARN bevy_ecs::schedule::schedule: Schedule ExtractSchedule has ambiguities. 296 pairs of systems with conflicting data access have indeterminate execution order. Consider adding `before`, `after`, or `ambiguous_with` relationships between these: -- bevy_render::extract_component::extract_components<bevy_sprite::SpriteSource> and bevy_render::render_asset::extract_render_asset<bevy_sprite::mesh2d::material::PreparedMaterial2d<bevy_sprite::mesh2d::color_material::ColorMaterial>> ``` </p> </details> To try locally: ```sh CI_TESTING_CONFIG="./.github/example-run/ambiguity_detection.ron" cargo run --example ambiguity_detection --features "bevy_ci_testing,trace,trace_chrome" ``` --------- Co-authored-by: Jan Hohenheim <jan@hohenheim.ch> |