forked from bevyengine/bevy
-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
No archetype component access #1
Open
BD103
wants to merge
595
commits into
Pretenvy:main
Choose a base branch
from
chescock:no-archetype-component-access
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CodSpeed Performance ReportMerging #1 will improve performances by ×3.5Comparing Summary
Benchmarks breakdown
|
# Objective - Make working with `bevy_time` more ergonomic in `no_std` environments. Currently `bevy_time` expects the getter in environments where time can't be obtained automatically via the instruction set or the standard library to be of type `*mut fn() -> Duration`. [`fn()`](https://doc.rust-lang.org/beta/std/primitive.fn.html) is already a function pointer, so `*mut fn()` is a _pointer to a function pointer_. This is harder to use and error prone since creating a pointer out of something like `&mut fn() -> Duration` when the lifetime of the reference isn't static will lead to an undefined behavior once the reference is freed ## Solution - Accept a `fn() -> Duration` instead ## Testing - I made a whole game on the Playdate that relies on `bevy_time` heavily, see: [bevydate_time](https://github.com/Mathspy/bevydate/blob/1b4f02adcde079cf9757fd3c7d20331c9ab04513/src/lib.rs#L510-L546) for usage of the Instant's getter. --- ## Showcase <details> <summary>Click to view showcase</summary> https://github.com/user-attachments/assets/f687847f-6b62-4322-95f3-c908ada3db30 </details> ## Migration Guide This is a breaking change but it's not for people coming from Bevy v0.15 ### Small thank you note Thanks to my friend https://github.com/repnop for helping me understand how to deal with function pointers in `unsafe` environments Co-authored-by: Wesley Norris <[email protected]>
# Objective - Fix off by one error introduced in bevyengine#17540 causing: ``` Cursor image StrongHandle<Image>{ id: Index(AssetIndex { generation: 0, index: 3 }), path: Some(cursors/kenney_crosshairPack/Tilesheet/crosshairs_tilesheet_white.png) } is invalid: The specified hotspot (64, 64) is outside the image bounds (64x64). ``` - First PR commit and run shows the bug: https://github.com/bevyengine/bevy/actions/runs/13009405866/job/36283507530?pr=17571 - Second PR commit fixes it. ## Solution - Hotspot coordinates are 0-indexed, so we need to subtract 1 from the width and height. ## Testing - Fix the tests which included the off-by-one error in their expected values. - Consolidate the tests into a single test for brevity. - Test round trip transform to ensure we can "undo" to get back to the original value. - Add a specific bounds test. - Ran the example again and observed there are no more error logs: `cargo run --example custom_cursor_image --features=custom_cursor`.
…vyengine#17592) This reverts commit ae52222. # Objective Fixes bevyengine#16856 ## Solution Remove rounding from `OrthographicProjection::update`, which was causing the center of the orthographic projection to be off center. ## Testing Ran the examples mentioned on bevyengine#16856 and code from bevyengine#16773 ## Showcase `orthographic` example  `projection_zoom` example  `camera_sub_view` example  `custom_primitives` example  bevyengine#16773 code 
# Objective Expose accessor functions to the `ObserverDescriptor`, so that users can use the `Observer` component to inspect what the observer is watching. This would be useful for me, I don't think there's any reason to hide these.
# Objective We have default query filters now, but there is no first-party marker for entity disabling yet Fixes bevyengine#17458 ## Solution Add the marker, cool recursive features and/or potential hook changes should be follow up work ## Testing Added a unit test to check that the new marker is enabled by default
# Objective Currently, `prepare_sprite_image_bind_group` spawns sprite batches onto an individual representative entity of the batch. This poses significant problems for multi-camera setups, since an entity may appear in multiple phase instances. ## Solution Instead, move batches into a resource that is keyed off the view and the representative entity. Long term we should switch to mesh2d and use the existing BinnedRenderPhase functionality rather than naively queueing into transparent and doing our own ad-hoc batching logic. Fixes bevyengine#16867, bevyengine#17351 ## Testing Tested repros in above issues.
# Objective ``` cargo test --package bevy_ecs --lib --all-features ``` fails to compile, with output like > error[E0433]: failed to resolve: could not find `Serialize` in `serde` > --> crates/bevy_ecs/src/entity/index_set.rs:14:69 > | > 14 | #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] > | ^^^^^^^^^ could not find `Serialize` in `serde` > | > note: found an item that was configured out > --> /home/alice/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.217/src/lib.rs:343:37 > | > 343 | pub use serde_derive::{Deserialize, Serialize}; > | ^^^^^^^^^ > note: the item is gated behind the `serde_derive` feature > --> /home/alice/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.217/src/lib.rs:341:7 > | > 341 | #[cfg(feature = "serde_derive")] > | ^^^^^^^^^^^^^^^^^^^^^^^^ ## Solution Add the required feature flags and get bevy_ecs compiling standalone corrctly. ## Testing The command above now compiles succesfully. Note that several system stepping tests are failing, and were not being tested in CI. That's a different PR's problem though.
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.29.4 to 1.29.5. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/crate-ci/typos/releases">crate-ci/typos's releases</a>.</em></p> <blockquote> <h2>v1.29.5</h2> <h2>[1.29.5] - 2025-01-30</h2> <h3>Internal</h3> <ul> <li>Update a dependency</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/crate-ci/typos/blob/master/CHANGELOG.md">crate-ci/typos's changelog</a>.</em></p> <blockquote> <h2>[1.29.5] - 2025-01-30</h2> <h3>Internal</h3> <ul> <li>Update a dependency</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/crate-ci/typos/commit/11ca4583f2f3f74c7e7785c0ecb20fe2c99a4308"><code>11ca458</code></a> chore: Release</li> <li><a href="https://github.com/crate-ci/typos/commit/99fd37f157f55c0565a0574a86eb3949dbd38165"><code>99fd37f</code></a> docs: Update changelog</li> <li><a href="https://github.com/crate-ci/typos/commit/4f604f6effffe7f41833b65ee75da75d416821ef"><code>4f604f6</code></a> Merge pull request <a href="https://redirect.github.com/crate-ci/typos/issues/1220">#1220</a> from epage/w7</li> <li><a href="https://github.com/crate-ci/typos/commit/ba04a1a0fd67a0e00ad36c5c5655b9740ee5e68a"><code>ba04a1a</code></a> perf: Remove ErrMode overhead</li> <li><a href="https://github.com/crate-ci/typos/commit/60452b5a81caa4f70c81282f2cdd2116fc045f52"><code>60452b5</code></a> chore: Update to Winnow 0.7</li> <li><a href="https://github.com/crate-ci/typos/commit/4c22f194b5c24cf2b7d0524df0857f0f8bbc32a5"><code>4c22f19</code></a> refactor: Migrate from Parser to ModalParser</li> <li><a href="https://github.com/crate-ci/typos/commit/7830eb8730de84bf14bc14cadb996c0e52f9fe93"><code>7830eb8</code></a> refactor: Resolve deprecations</li> <li><a href="https://github.com/crate-ci/typos/commit/07f1292e290f35153fb91dad3324e7bdb9cd827a"><code>07f1292</code></a> chore: Upgrade to Winnow 0.6.26</li> <li><a href="https://github.com/crate-ci/typos/commit/3683264986a72f63f13e9e8fc132a13af2a322b8"><code>3683264</code></a> chore(deps): Update Rust Stable to v1.84 (<a href="https://redirect.github.com/crate-ci/typos/issues/1216">#1216</a>)</li> <li><a href="https://github.com/crate-ci/typos/commit/2ed38e07fc83ec249f9736b81008690c2c88ec98"><code>2ed38e0</code></a> chore(deps): Update Rust crate bstr to v1.11.3 (<a href="https://redirect.github.com/crate-ci/typos/issues/1202">#1202</a>)</li> <li>See full diff in <a href="https://github.com/crate-ci/typos/compare/v1.29.4...v1.29.5">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
# Objective Revert bevyengine#17631 After some more experimentation, realised it's not the right approach.
# Objective Our [`TextSpan`](https://docs.rs/bevy/latest/bevy/prelude/struct.TextSpan.html) docs include a code example that does not actually "work." The code silently does not render anything, and the `Text*Writer` helpers fail. This seems to be by design, because we can't use `Text` or `Text2d` from `bevy_ui` or `bevy_sprite` within docs in `bevy_text`. (Correct me if I am wrong) I have seen multiple users confused by these docs. Also fixes bevyengine#16794 ## Solution Remove the code example from `TextSpan`, and instead encourage users to seek docs on `Text` or `Text2d`. Add examples with nested `TextSpan`s in those areas.
) # Objective The method `World::as_unsafe_world_cell_readonly` is used to create an `UnsafeWorldCell` which is only allowed to access world data immutably. This can be tricky to use, as the data that an `UnsafeWorldCell` is allowed to access exists only in documentation (you could think of it as a "doc-time abstraction" rather than a "compile-time" abstraction). It's quite easy to forget where a particular instance came from and attempt to use it for mutable access, leading to instant, silent undefined behavior. ## Solution Add a debug-mode only flag to `UnsafeWorldCell` which tracks whether or not the instance can be used to access world data mutably. This should catch basic improper usages of `as_unsafe_world_cell_readonly`. ## Future work There are a few ways that you can bypass the runtime checks introduced by this PR: * Any world accesses done via `UnsafeWorldCell::storages` are completely invisible to these runtime checks. Unfortunately, `storages` constitutes most of the world accesses used in the engine itself, so this PR will mostly benefit downstream users of bevy. * It's possible to call `get_resource_by_id`, and then convert the returned `Ptr` to a `PtrMut` by calling `assert_unique`. In the future we'll probably want to add a debug-mode only flag to `Ptr` which tracks whether or not it can be upgraded to a `PtrMut`. I didn't include this change in this PR as those types are currently defined using macros which makes it a bit tricky to modify their definitions. * Any data accesses done through a mutable `UnsafeWorldCell` are completely unchecked, meaning it's possible to unsoundly create multiple mutable references to a single component, for example. In the future we may want to store an `Access<>` set inside of the world's `Storages` to add granular debug-mode runtime checks. That said, I'd consider this PR to be a good first step towards adding full runtime checks to `UnsafeWorldCell`. ## Testing Added a few tests that basic invalid mutable world access result in a panic. --------- Co-authored-by: Joseph <[email protected]> Co-authored-by: Alice I Cecile <[email protected]>
# Objective - Fix the atmosphere LUT parameterization in the aerial -view and sky-view LUTs - Correct the light accumulation according to a ray-marched reference - Avoid negative values of the sun disk illuminance when the sun disk is below the horizon ## Solution - Adding a Newton's method iteration to `fast_sqrt` function - Switched to using `fast_acos_4` for better precision of the sun angle towards the horizon (view mu angle = 0) - Simplified the function for mapping to and from the Sky View UV coordinates by removing an if statement and correctly apply the method proposed by the [Hillarie paper](https://sebh.github.io/publications/egsr2020.pdf) detailed in section 5.3 and 5.4. - Replaced the `ray_dir_ws.y` term with a shadow factor in the `sample_sun_illuminance` function that correctly approximates the sun disk occluded by the earth from any view point ## Testing - Ran the atmosphere and SSAO examples to make sure the shaders still compile and run as expected. --- ## Showcase <img width="1151" alt="showcase-img" src="https://github.com/user-attachments/assets/de875533-42bd-41f9-9fd0-d7cc57d6e51c" /> --------- Co-authored-by: Emerson Coskey <[email protected]>
# Objective - Most of the `*MeshBuilder` classes are not implementing `Reflect` ## Solution - Implementing `Reflect` for all `*MeshBuilder` were is possible. - Make sure all `*MeshBuilder` implements `Default`. - Adding new `MeshBuildersPlugin` that registers all `*MeshBuilder` types. ## Testing - `cargo run -p ci` - Tested some examples like `3d_scene` just in case something was broken.
# Objective Prevent unsound uses of `DeferredWorld` as a `SystemParam`. It is currently unsound because it does not check for existing access, and because it incorrectly registers filtered access. ## Solution Have `DeferredWorld` panic if a previous parameter has conflicting access. Have `DeferredWorld` update `archetype_component_access` so that the multi-threaded executor sees the access. Fix `FilteredAccessSet::read_all()` and `write_all()` to correctly add a `FilteredAccess` with no filter so that `Query` is able to detect the conflicts. Remove redundant `read_all()` call, since `write_all()` already declares read access. Remove unnecessary `set_has_deferred()` call, since `<DeferredWorld as SystemParam>::apply_deferred()` does nothing. Previously we were inserting unnecessary `apply_deferred` systems in the schedule. ## Testing Added unit tests for systems where `DeferredWorld` conflicts with a `Query` in the same system.
… (alternative to bevyengine#17604) (bevyengine#17646) # Objective - When obtaining an axis from the transform and putting that into `Transform::rotate_axis` or `Transform::rotate_axis_local`, floating point errors could accumulate exponentially, resulting in denormalized rotation. - This is an alternative to and closes bevyengine#17604, due to lack of consent around this in the [discord discussion](https://discord.com/channels/691052431525675048/1203087353850364004/1334232710658392227) - Closes bevyengine#16480 ## Solution - Add a warning of this issue and a recommendation to normalize to the API docs. - Add a runtime warning that checks for denormalized axis in debug mode, with a reference to the API docs.
# Objective Allow mapping `Mut` to another value while returning a custom error on failure. ## Solution Added `try_map_unchanged` to `Mut` which returns a `Result` instead of `Option` .
# Objective While working on bevyengine#17649, I found the docs for `WorldQuery` and the related traits frustratingly vague. ## Solution Clarify them and add some more tangible advice. Also fix a copy-pasted typo in related comments. --------- Co-authored-by: James O'Brien <[email protected]>
…about the first batch in each batch set. (bevyengine#17680) Data for the other batches is only accessed by the GPU, not the CPU, so it's a waste of time and memory to store information relating to those other batches. On Bistro, this reduces time spent in `batch_and_prepare_binned_render_phase` from 85.9 us to 61.2 us, a 40% speedup. 
# Objective Fix text 2d. Fixes bevyengine#17670 ## Solution Evidently there's a 1:N extraction going on here that requires using the render entity rather than main entity. ## Testing Text 2d example
…7689) # Objective - Fixes CI failure due to `uuid` 1.13 using the new version of `getrandom` which requires using a new API to work on Wasm. ## Solution - Based on [`uuid` 1.13 release notes](https://github.com/uuid-rs/uuid/releases/tag/1.13.0) I've enabled the `js` feature on `wasm32`. This will need to be revisited once bevyengine#17499 is up for review - Updated minimum `uuid` version to 1.13.1, which fixes a separate issue with `target_feature = atomics` on `wasm`. ## Testing - `cargo check --target wasm32-unknown-unknown`
… change. (bevyengine#17688) This patch fixes a bug whereby we're re-extracting every mesh every frame. It's a regression from PR bevyengine#17413. The code in question has actually been in the tree with this bug for quite a while; it's that just the code didn't actually run unless the renderer considered the previous view transforms necessary. Occlusion culling expanded the set of circumstances under which Bevy computes the previous view transforms, causing this bug to appear more often. This patch fixes the issue by checking to see if the previous transform of a mesh actually differs from the current transform before copying the current transform to the previous transform.
We were calling `clear()` on the work item buffer table, which caused us to deallocate all the CPU side buffers. This patch changes the logic to instead just clear the buffers individually, but leave their backing stores. This has two consequences: 1. To effectively retain work item buffers from frame to frame, we need to key them off `RetainedViewEntity` values and not the render world `Entity`, which is transient. This PR changes those buffers accordingly. 2. We need to clean up work item buffers that belong to views that went away. Amusingly enough, we actually have a system, `delete_old_work_item_buffers`, that tries to do this already, but it wasn't doing anything because the `clear_batched_gpu_instance_buffers` system already handled that. This patch actually makes the `delete_old_work_item_buffers` system useful, by removing the clearing behavior from `clear_batched_gpu_instance_buffers` and instead making `delete_old_work_item_buffers` delete buffers corresponding to nonexistent views. On Bistro, this PR improves the performance of `batch_and_prepare_binned_render_phase` from 61.2 us to 47.8 us, a 28% speedup. 
# Objective Follow-up to bevyengine#17549 and bevyengine#16547. A large part of `Vec`s usefulness is behind its ability to be sliced, like sorting f.e., so we want the same to be possible for `UniqueEntityVec`. ## Solution Add a `UniqueEntitySlice` type. It is a wrapper around `[T]`, and itself a DST. Because `mem::swap` has a `Sized` bound, DSTs cannot be swapped, and we can freely hand out mutable subslices without worrying about the uniqueness invariant of the backing collection! `UniqueEntityVec` and the relevant `UniqueEntityIter`s now have methods and trait impls that return `UniqueEntitySlice`s. `UniqueEntitySlice` itself can deref into normal slices, which means we can avoid implementing the vast majority of immutable slice methods. Most of the remaining methods: - split a slice/collection in further unique subsections/slices - reorder the slice: `sort`, `rotate_*`, `swap` - construct/deconstruct/convert pointer-like types: `Box`, `Arc`, `Rc`, `Cow` - are comparison trait impls As this PR is already larger than I'd like, we leave several things to follow-ups: - `UniqueEntityArray` and the related slice methods that would return it - denoted by "chunk", "array_*" for iterators - Methods that return iterators with `UniqueEntitySlice` as their item - `windows`, `chunks` and `split` families - All methods that are capable of actively mutating individual elements. While they could be offered unsafely, subslicing makes their safety contract weird enough to warrant its own discussion. - `fill_with`, `swap_with_slice`, `iter_mut`, `split_first/last_mut`, `select_nth_unstable_*` Note that `Arc`, `Rc` and `Cow` are not fundamental types, so even if they contain `UniqueEntitySlice`, we cannot write direct trait impls for them. On top of that, `Cow` is not a receiver (like `self: Arc<Self>` is) so we cannot write inherent methods for it either.
# Cold Specialization ## Objective An ongoing part of our quest to retain everything in the render world, cold-specialization aims to cache pipeline specialization so that pipeline IDs can be recomputed only when necessary, rather than every frame. This approach reduces redundant work in stable scenes, while still accommodating scenarios in which materials, views, or visibility might change, as well as unlocking future optimization work like retaining render bins. ## Solution Queue systems are split into a specialization system and queue system, the former of which only runs when necessary to compute a new pipeline id. Pipelines are invalidated using a combination of change detection and ECS ticks. ### The difficulty with change detection Detecting “what changed” can be tricky because pipeline specialization depends not only on the entity’s components (e.g., mesh, material, etc.) but also on which view (camera) it is rendering in. In other words, the cache key for a given pipeline id is a view entity/render entity pair. As such, it's not sufficient simply to react to change detection in order to specialize -- an entity could currently be out of view or could be rendered in the future in camera that is currently disabled or hasn't spawned yet. ### Why ticks? Ticks allow us to ensure correctness by allowing us to compare the last time a view or entity was updated compared to the cached pipeline id. This ensures that even if an entity was out of view or has never been seen in a given camera before we can still correctly determine whether it needs to be re-specialized or not. ## Testing TODO: Tested a bunch of different examples, need to test more. ## Migration Guide TODO - `AssetEvents` has been moved into the `PostUpdate` schedule. --------- Co-authored-by: Patrick Walton <[email protected]>
# Objective Simplify and expand the API for `QueryState`. `QueryState` has a lot of methods that mirror those on `Query`. These are then multiplied by variants that take `&World`, `&mut World`, and `UnsafeWorldCell`. In addition, many of them have `_manual` variants that take `&QueryState` and avoid calling `update_archetypes()`. Not all of the combinations exist, however, so some operations are not possible. ## Solution Introduce methods to get a `Query` from a `QueryState`. That will reduce duplication between the types, and ensure that the full `Query` API is always available for `QueryState`. Introduce methods on `Query` that consume the query to return types with the full `'w` lifetime. This avoids issues with borrowing where things like `query_state.query(&world).get(entity)` don't work because they borrow from the temporary `Query`. Finally, implement `Copy` for read-only `Query`s. `get_inner` and `iter_inner` currently take `&self`, so changing them to consume `self` would be a breaking change. By making `Query: Copy`, they can consume a copy of `self` and continue to work. The consuming methods also let us simplify the implementation of methods on `Query`, by doing `fn foo(&self) { self.as_readonly().foo_inner() }` and `fn foo_mut(&mut self) { self.reborrow().foo_inner() }`. That structure makes it more difficult to accidentally extend lifetimes, since the safe `as_readonly()` and `reborrow()` methods shrink them appropriately. The optimizer is able to see that they are both identity functions and inline them, so there should be no performance cost. Note that this change would conflict with bevyengine#15848. If `QueryState` is stored as a `Cow`, then the consuming methods cannot be implemented, and `Copy` cannot be implemented. ## Future Work The next step is to mark the methods on `QueryState` as `#[deprecated]`, and move the implementations into `Query`. ## Migration Guide `Query::to_readonly` has been renamed to `Query::as_readonly`.
# Objective The feature gates for the `UiChildren` and `UiRootNodes` system params make the unconstructable `GhostNode` `PhantomData` trick redundant. ## Solution Remove the `GhostNode::new` method and change `GhostNode` into a unit struct. ## Testing ```cargo run --example ghost_nodes``` still works
…17679) # Objective Fixes bevyengine#17662 ## Solution Moved `Item` and `fetch` from `WorldQuery` to `QueryData`, and adjusted their implementations accordingly. Currently, documentation related to `fetch` is written under `WorldQuery`. It would be more appropriate to move it to the `QueryData` documentation for clarity. I am not very experienced with making contributions. If there are any mistakes or areas for improvement, I would appreciate any suggestions you may have. ## Migration Guide The `WorldQuery::Item` type and `WorldQuery::fetch` method have been moved to `QueryData`, as they were not useful for `QueryFilter` types. --------- Co-authored-by: Chris Russell <[email protected]>
# Objective Fix two minor typos in bevy_picking docs.
# Objective Basic `TextShadow` support. ## Solution New `TextShadow` component with `offset` and `color` fields. Just insert it on a `Text` node to add a shadow. New system `extract_text_shadows` handles rendering. It's not "real" shadows just the text redrawn with an offset and a different colour. Blur-radius support will need changes to the shaders and be a lot more complicated, whereas this still looks okay and took a couple of minutes to implement. I added the `TextShadow` component to `bevy_ui` rather than `bevy_text` because it only supports the UI atm. We can add a `Text2d` version in a followup but getting the same effect in `Text2d` is trivial even without official support. --- ## Showcase <img width="122" alt="text_shadow" src="https://github.com/user-attachments/assets/0333d167-c507-4262-b93b-b6d39e2cf3a4" /> <img width="136" alt="g" src="https://github.com/user-attachments/assets/9b01d5d9-55c9-4af7-9360-a7b04f55944d" />
# Objective Fix the doc comment for `Val::resolve`. It doesn't return a value in logical pixels unless the inputs are also in logical pixels.
# Objective - Fixes bevyengine#18129 ## Solution - Update ruzstd requirement from 0.7.0 to 0.8.0 and fixed imports. --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
# Objective - Closes bevyengine#18131 ## Solution - Update ureq requirement from 2.10.1 to 3.0.8 and migrate breaking code. --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
# Objective - Fixes the issue described in this comment: bevyengine#16680 (comment). ## Solution - Cache one-shot systems by `S: IntoSystem` (which is const-asserted to be a ZST) rather than `S::System`. ## Testing Added a new unit test named `cached_system_into_same_system_type` to `system_registry.rs`. --- ## Migration Guide The `CachedSystemId` resource has been changed: ```rust // Before: let cached_id = CachedSystemId::<S::System>(id); assert!(id == cached_id.0); // After: let cached_id = CachedSystemId::<S>::new(id); assert!(id == SystemId::from_entity(cached_id.entity)); ```
# Objective Transparently uses simple `EnvironmentMapLight`s to mimic `AmbientLight`s. Implements the first part of bevyengine#17468, but I can implement hemispherical lights in this PR too if needed. ## Solution - A function `EnvironmentMapLight::solid_color(&mut Assets<Image>, Color)` is provided to make an environment light with a solid color. - A new system is added to `SimulationLightSystems` that maps `AmbientLight`s on views or the world to a corresponding `EnvironmentMapLight`. I have never worked with (or on) Bevy before, so nitpicky comments on how I did things are appreciated :). ## Testing Testing was done on a modified version of the `3d/lighting` example, where I removed all lights except the ambient light. I have not included the example, but can if required. ## Migration `bevy_pbr::AmbientLight` has been deprecated, so all usages of it should be replaced by a `bevy_pbr::EnvironmentMapLight` created with `EnvironmentMapLight::solid_color` placed on the camera. There is no alternative to ambient lights as resources.
# Objective Work for issue bevyengine#17682 What's in this PR: * Removal of some `!Send` resources that Bevy uses internally * Replaces `!Send` resources with `thread_local!` static What this PR does not cover: * The ability to create `!Send` resources still exists * Tests that test `!Send` resources are present (and should not be removed until the ability to create `!Send` resources is removed) * The example `log_layers_ecs` still uses a `!Send` resource. In this example, removing the `!Send` resource results in the system that uses it running on a thread other than the main thread, which doesn't work with lazily initialized `thread_local!` static data. Removing this `!Send` resource will need to be deferred until the System API is extended to support configuring which thread the System runs on. Once an issue for this work is created, it will be mentioned in bevyengine#17667 Once the System API is extended to allow control of which thread the System runs on, the rest of the `!Send` resources can be removed in a different PR.
# Objective Minimal implementation of directed one-to-one relationships via implementing `RelationshipSourceCollection` for `Entity`. Now you can do ```rust #[derive(Component)] #[relationship(relationship_target = Below)] pub struct Above(Entity); #[derive(Component)] #[relationship_target(relationship = Above)] pub struct Below(Entity); ``` ## Future Work It would be nice if the relationships could be fully symmetrical in the future - in the example above, since `Above` is the source of truth you can't add `Below` to an entity and have `Above` added automatically. ## Testing Wrote unit tests for new relationship sources and and verified adding/removing relationships maintains connection as expected.
# Objective Fixes bevyengine#18117 These are component subtraits, but unlike for `Event` (before that bound got removed) this still shows it as a component because it's actually used as such. ## Testing ```sh RUSTDOCFLAGS="--html-after-content docs-rs/trait-tags.html --cfg docsrs_dep" RUSTFLAGS="--cfg docsrs_dep" cargo doc --no-deps --package bevy_ecs ``` --- ## Showcase 
# Objective The doc comment for `BorderRadius::resolve_single_corner` returns a value in physical pixels but the doc comments implies it returns a logical value.
# Objective - Fixes bevyengine#16339 ## Solution - Replaced `component_reads_and_writes` and `component_writes` with `try_iter_component_access`. ## Testing - Ran `dynamic` example to confirm behaviour is unchanged. - CI --- ## Migration Guide The following methods (some removed in previous PRs) are now replaced by `Access::try_iter_component_access`: * `Access::component_reads_and_writes` * `Access::component_reads` * `Access::component_writes` As `try_iter_component_access` returns a `Result`, you'll now need to handle the failing case (e.g., `unwrap()`). There is currently a single failure mode, `UnboundedAccess`, which occurs when the `Access` is for all `Components` _except_ certain exclusions. Since this list is infinite, there is no meaningful way for `Access` to provide an iterator. Instead, get a list of components (e.g., from the `Components` structure) and iterate over that instead, filtering using `Access::has_component_read`, `Access::has_component_write`, etc. Additionally, you'll need to `filter_map` the accesses based on which method you're attempting to replace: * `Access::component_reads_and_writes` -> `Exclusive(_) | Shared(_)` * `Access::component_reads` -> `Shared(_)` * `Access::component_writes` -> `Exclusive(_)` To ease migration, please consider the below extension trait which you can include in your project: ```rust pub trait AccessCompatibilityExt { /// Returns the indices of the components this has access to. fn component_reads_and_writes(&self) -> impl Iterator<Item = T> + '_; /// Returns the indices of the components this has non-exclusive access to. fn component_reads(&self) -> impl Iterator<Item = T> + '_; /// Returns the indices of the components this has exclusive access to. fn component_writes(&self) -> impl Iterator<Item = T> + '_; } impl<T: SparseSetIndex> AccessCompatibilityExt for Access<T> { fn component_reads_and_writes(&self) -> impl Iterator<Item = T> + '_ { self .try_iter_component_access() .expect("Access is unbounded. Please refactor the usage of this method to directly use try_iter_component_access") .filter_map(|component_access| { let index = component_access.index().sparse_set_index(); match component_access { ComponentAccessKind::Archetypal(_) => None, ComponentAccessKind::Shared(_) => Some(index), ComponentAccessKind::Exclusive(_) => Some(index), } }) } fn component_reads(&self) -> impl Iterator<Item = T> + '_ { self .try_iter_component_access() .expect("Access is unbounded. Please refactor the usage of this method to directly use try_iter_component_access") .filter_map(|component_access| { let index = component_access.index().sparse_set_index(); match component_access { ComponentAccessKind::Archetypal(_) => None, ComponentAccessKind::Shared(_) => Some(index), ComponentAccessKind::Exclusive(_) => None, } }) } fn component_writes(&self) -> impl Iterator<Item = T> + '_ { self .try_iter_component_access() .expect("Access is unbounded. Please refactor the usage of this method to directly use try_iter_component_access") .filter_map(|component_access| { let index = component_access.index().sparse_set_index(); match component_access { ComponentAccessKind::Archetypal(_) => None, ComponentAccessKind::Shared(_) => None, ComponentAccessKind::Exclusive(_) => Some(index), } }) } } ``` Please take note of the use of `expect(...)` in these methods. You should consider using these as a starting point for a more appropriate migration based on your specific needs. ## Notes - This new method is fallible based on whether the `Access` is bounded or unbounded (unbounded occurring with inverted component sets). If bounded, will return an iterator of every item and its access level. I believe this makes sense without exposing implementation details around `Access`. - The access level is defined by an `enum` `ComponentAccessKind<T>`, either `Archetypical`, `Shared`, or `Exclusive`. As a convenience, this `enum` has a method `index` to get the inner `T` value without a match statement. It does add more code, but the API is clearer. - Within `QueryBuilder` this new method simplifies several pieces of logic without changing behaviour. - Within `QueryState` the logic is simplified and the amount of iteration is reduced, potentially improving performance. - Within the `dynamic` example it has identical behaviour, with the inversion footgun explicitly highlighted by an `unwrap`. --------- Co-authored-by: Chris Russell <[email protected]> Co-authored-by: Mike <[email protected]>
# Objective - have a testbed for UI ## Solution - move previous `ui` example to `full_ui` - add a testbed ui with several scenes - `ui_layout_rounding` is one of those scenes, so remove it as a standalone example the previous `ui` / new `full_ui` is I think still useful as it has some things like scroll, debug ui that are not shown anywhere else
…#17996) # Objective Many systems like `Schedule` rely on the fact that every structural ECS changes are deferred until an exclusive system flushes the `World` itself. This gives us the benefits of being able to run systems in parallel without worrying about dangling references caused by memory (re)allocations, which will in turn lead to **Undefined Behavior**. However, this isn't explicitly documented in `SystemParam`; currently it only vaguely hints that in `init_state`, based on the fact that structural ECS changes require mutable access to the _whole_ `World`. ## Solution Document this behavior explicitly in `SystemParam`'s type-level documentations.
…only()` (bevyengine#17973) # Objective Fix unsound query transmutes on queries obtained from `Query::as_readonly()`. The following compiles, and the call to `transmute_lens()` should panic, but does not: ```rust fn bad_system(query: Query<&mut A>) { let mut readonly = query.as_readonly(); let mut lens: QueryLens<&mut A> = readonly.transmute_lens(); let other_readonly: Query<&A> = query.as_readonly(); // `lens` and `other_readonly` alias, and are both alive here! } ``` To make `Query::as_readonly()` zero-cost, we pointer-cast `&QueryState<D, F>` to `&QueryState<D::ReadOnly, F>`. This means that the `component_access` for a read-only query's state may include accesses for the original mutable version, but the `Query` does not have exclusive access to those components! `transmute` and `join` use that access to ensure that a join is valid, and will incorrectly allow a transmute that includes mutable access. As a bonus, allow `Query::join`s that output `FilteredEntityRef` or `FilteredEntityMut` to receive access from the `other` query. Currently they only receive access from `self`. ## Solution When transmuting or joining from a read-only query, remove any writes before performing checking that the transmute is valid. For joins, be sure to handle the case where one input query was the result of `as_readonly()` but the other has valid mutable access. This requires identifying read-only queries, so add a `QueryData::IS_READ_ONLY` associated constant. Note that we only call `QueryState::as_transmuted_state()` with `NewD: ReadOnlyQueryData`, so checking for read-only queries is sufficient to check for `as_transmuted_state()`. Removing writes requires allocating a new `FilteredAccess`, so only do so if the query is read-only and the state has writes. Otherwise, the existing access is correct and we can continue using a reference to it. Use the new read-only state to call `NewD::set_access`, so that transmuting to a `FilteredAccessMut` results in a read-only `FilteredAccessMut`. Otherwise, it would take the original write access, and then the transmute would panic because it had too much access. Note that `join` was previously passing `self.component_access` to `NewD::set_access`. Switching it to `joined_component_access` also allows a join that outputs `FilteredEntity(Ref|Mut)` to receive access from `other`. The fact that it didn't do that before seems like an oversight, so I didn't try to prevent that change. ## Testing Added unit tests with the unsound transmute and join.
…yengine#17858) Fixes bevyengine#17720 ## Objective Spawning RelationshipTargets from scenes currently fails to preserve RelationshipTarget ordering (ex: `Children` has an arbitrary order). This is because it uses the normal hook flow to set up the collection, which means we are pushing onto the collection in _spawn order_ (which is currently in archetype order, which will often produce mismatched orderings). We need to preserve the ordering in the original RelationshipTarget collection. Ideally without expensive checking / fixups. ## Solution One solution would be to spawn in hierarchy-order. However this gets complicated as there can be multiple hierarchies, and it also means we can't spawn in more cache-friendly orders (ex: the current per-archetype spawning, or future even-smarter per-table spawning). Additionally, same-world cloning has _slightly_ more nuanced needs (ex: recursively clone linked relationships, while maintaining _original_ relationships outside of the tree via normal hooks). The preferred approach is to directly spawn the remapped RelationshipTarget collection, as this trivially preserves the ordering. Unfortunately we can't _just_ do that, as when we spawn the children with their Relationships (ex: `ChildOf`), that will insert a duplicate. We could "fixup" the collection retroactively by just removing the back half of duplicates, but this requires another pass / more lookups / allocating twice as much space. Additionally, it becomes complicated because observers could insert additional children, making it harder (aka more expensive) to determine which children are dupes and which are not. The path I chose is to support "opting out" of the relationship target hook in the contexts that need that, as this allows us to just cheaply clone the mapped collection. The relationship hook can look for this configuration when it runs and skip its logic when that happens. A "simple" / small-amount-of-code way to do this would be to add a "skip relationship spawn" flag to World. Sadly, any hook / observer that runs _as the result of an insert_ would also read this flag. We really need a way to scope this setting to a _specific_ insert. Therefore I opted to add a new `RelationshipInsertHookMode` enum and an `entity.insert_with_relationship_insert_hook_mode` variant. Obviously this is verbose and ugly. And nobody wants _more_ insert variants. But sadly this was the best I could come up with from a performance and capability perspective. If you have alternatives let me know! There are three variants: 1. `RelationshipInsertHookMode::Run`: always run relationship insert hooks (this is the default) 2. `RelationshipInsertHookMode::Skip`: do not run any relationship insert hooks for this insert (this is used by spawner code) 3. `RelationshipInsertHookMode::RunIfNotLinked`: only run hooks for _unlinked_ relationships (this is used in same-world recursive entity cloning to preserve relationships outside of the deep-cloned tree) Note that I have intentionally only added "insert with relationship hook mode" variants to the cases we absolutely need (everything else uses the default `Run` mode), just to keep the code size in check. I do not think we should add more without real _very necessary_ use cases. I also made some other minor tweaks: 1. I split out `SourceComponent` from `ComponentCloneCtx`. Reading the source component no longer needlessly blocks mutable access to `ComponentCloneCtx`. 2. Thanks to (1), I've removed the `RefCell` wrapper over the cloned component queue. 3. (1) also allowed me to write to the EntityMapper while queuing up clones, meaning we can reserve entities during the component clone and write them to the mapper _before_ inserting the component, meaning cloned collections can be mapped on insert. 4. I've removed the closure from `write_target_component_ptr` to simplify the API / make it compatible with the split `SourceComponent` approach. 5. I've renamed `EntityCloner::recursive` to `EntityCloner::linked_cloning` to connect that feature more directly with `RelationshipTarget::LINKED_SPAWN` 6. I've removed `EntityCloneBehavior::RelationshipTarget`. This was always intended to be temporary, and this new behavior removes the need for it. --------- Co-authored-by: Viktor Gustavsson <[email protected]>
…e#17482)" (bevyengine#18167) This reverts commit 0b5302d. # Objective - Fixes bevyengine#18158 - bevyengine#17482 introduced rendering changes and was merged a bit too fast ## Solution - Revert bevyengine#17482 so that it can be redone and rendering changes discussed before being merged. This will make it easier to compare changes with main in the known "valid" state This is not an issue with the work done in bevyengine#17482 that is still interesting
# Objective Component `require()` IDE integration is fully broken, as of bevyengine#16575. ## Solution This reverts us back to the previous "put the docs on Component trait" impl. This _does_ reduce the accessibility of the required components in rust docs, but the complete erasure of "required component IDE experience" is not worth the price of slightly increased prominence of requires in docs. Additionally, Rust Analyzer has recently started including derive attributes in suggestions, so we aren't losing that benefit of the proc_macro attribute impl.
## Objective `insert_or_spawn_batch` is due to be deprecated eventually (bevyengine#15704), and removing uses internally will make that easier. ## Solution Replaced internal uses of `insert_or_spawn_batch` with `try_insert_batch` (non-panicking variant because `insert_or_spawn_batch` didn't panic). All of the internal uses are in rendering code. Since retained rendering was meant to get rid non-opaque entity IDs, I assume the code was just using `insert_or_spawn_batch` because `insert_batch` didn't exist and not because it actually wanted to spawn something. However, I am *not* confident in my ability to judge rendering code.
…engine#18017) # Objective This PR adds: - function call hook attributes `#[component(on_add = func(42))]` - main feature of this commit - closure hook attributes `#[component(on_add = |w, ctx| { /* ... */ })]` - maybe too verbose - but was easy to add - was suggested on discord This allows to reuse common functionality without replicating a lot of boilerplate. A small example is a hook which just adds different default sprites. The sprite loading code would be the same for every component. Unfortunately we can't use the required components feature, since we need at least an `AssetServer` or other `Resource`s or `Component`s to load the sprite. ```rs fn load_sprite(path: &str) -> impl Fn(DeferredWorld, HookContext) { |mut world, ctx| { // ... use world to load sprite } } #[derive(Component)] #[component(on_add = load_sprite("knight.png"))] struct Knight; #[derive(Component)] #[component(on_add = load_sprite("monster.png"))] struct Monster; ``` --- The commit also reorders the logic of the derive macro a bit. It's probably a bit less lazy now, but the functionality shouldn't be performance critical and is executed at compile time anyways. ## Solution - Introduce `HookKind` enum in the component proc macro module - extend parsing to allow more cases of expressions ## Testing I have some code laying around. I'm not sure where to put it yet though. Also is there a way to check compilation failures? Anyways, here it is: ```rs use bevy::prelude::*; #[derive(Component)] #[component( on_add = fooing_and_baring, on_insert = fooing_and_baring, on_replace = fooing_and_baring, on_despawn = fooing_and_baring, on_remove = fooing_and_baring )] pub struct FooPath; fn fooing_and_baring( world: bevy::ecs::world::DeferredWorld, ctx: bevy::ecs::component::HookContext, ) { } #[derive(Component)] #[component( on_add = baring_and_bazzing("foo"), on_insert = baring_and_bazzing("foo"), on_replace = baring_and_bazzing("foo"), on_despawn = baring_and_bazzing("foo"), on_remove = baring_and_bazzing("foo") )] pub struct FooCall; fn baring_and_bazzing( path: &str, ) -> impl Fn(bevy::ecs::world::DeferredWorld, bevy::ecs::component::HookContext) { |world, ctx| {} } #[derive(Component)] #[component( on_add = |w,ctx| {}, on_insert = |w,ctx| {}, on_replace = |w,ctx| {}, on_despawn = |w,ctx| {}, on_remove = |w,ctx| {} )] pub struct FooClosure; #[derive(Component, Debug)] #[relationship(relationship_target = FooTargets)] #[component( on_add = baring_and_bazzing("foo"), // on_insert = baring_and_bazzing("foo"), // on_replace = baring_and_bazzing("foo"), on_despawn = baring_and_bazzing("foo"), on_remove = baring_and_bazzing("foo") )] pub struct FooTargetOf(Entity); #[derive(Component, Debug)] #[relationship_target(relationship = FooTargetOf)] #[component( on_add = |w,ctx| {}, on_insert = |w,ctx| {}, // on_replace = |w,ctx| {}, // on_despawn = |w,ctx| {}, on_remove = |w,ctx| {} )] pub struct FooTargets(Vec<Entity>); // MSG: mismatched types expected fn pointer `for<'w> fn(bevy::bevy_ecs::world::DeferredWorld<'w>, bevy::bevy_ecs::component::HookContext)` found struct `Bar` // // pub struct Bar; // #[derive(Component)] // #[component( // on_add = Bar, // )] // pub struct FooWrongPath; // MSG: this function takes 1 argument but 2 arguements were supplied // // #[derive(Component)] // #[component( // on_add = wrong_bazzing("foo"), // )] // pub struct FooWrongCall; // // fn wrong_bazzing(path: &str) -> impl Fn(bevy::ecs::world::DeferredWorld) { // |world| {} // } // MSG: expected 1 argument, found 2 // // #[derive(Component)] // #[component( // on_add = |w| {}, // )] // pub struct FooWrongCall; ``` --- ## Showcase I'll try to continue to work on this to have a small section in the release notes.
…yengine#10462) In 0.11 you could easily access the inverse model matrix inside a WGSL shader with `transpose(mesh.inverse_transpose_model)`. This was changed in 0.12 when `inverse_transpose_model` was removed and it's now not as straightfoward. I wrote a helper function for my own code and thought I'd submit a pull request in case it would be helpful to others.
# Objective Based on bevyengine#18054, this PR builds on bevyengine#18035 to deprecate: - `Commands::insert_or_spawn_batch` - `Entities::alloc_at_without_replacement` - `Entities::alloc_at` - `World::insert_or_spawn_batch` - `World::insert_or_spawn_batch_with_caller` ## Testing Just deprecation, so no new tests. Note that as of writing bevyengine#18035 is still under testing and review. ## Open Questions - [x] Should `entity::AllocAtWithoutReplacement` be deprecated? It is internal and only used in `Entities::alloc_at_without_replacement`. **EDIT:** Now deprecated. ## Migration Guide The following functions have been deprecated: - `Commands::insert_or_spawn_batch` - `World::insert_or_spawn_batch` - `World::insert_or_spawn_batch_with_caller` These functions, when used incorrectly, can cause major performance problems and are generally viewed as anti-patterns and foot guns. These are planned to be removed altogether in 0.17. Instead of these functions consider doing one of the following: Option A) Instead of despawing entities and re-spawning them at a particular id, insert the new `Disabled` component without despawning the entity, and use `try_insert_batch` or `insert_batch` and remove `Disabled` instead of re-spawning it. Option B) Instead of giving special meaning to an entity id, simply use `spawn_batch` and ensure entity references are valid when despawning. --------- Co-authored-by: JaySpruce <[email protected]> Co-authored-by: Alice Cecile <[email protected]>
… processing. (bevyengine#17216) # Objective - Today, enabling asset processing can generate many meta files. This makes it a painful transition for users as they get a "mega commit" containing tons of meta files. ## Solution - Stop automatically generating meta files! Users can just leave the meta files defaulted. - Add a function `AssetServer::write_default_meta_file_for_path` ## Testing - Tested this manually on the asset_processing example (by removing the meta files for the assets that had default meta files). - I did not add a unit test for the `write_default_meta_file_for_path` since we don't have an in-memory asset writer. Writing one could be useful in the future. --- ## Showcase Asset processing no longer automatically generates meta files! This makes it much easier to transition to using asset processing since you don't suddenly get many meta files when turning it on. You can still manually generate meta files using the new `AssetServer::write_default_meta_file_for_path` function.
…indows (bevyengine#18175) # Objective The fix in bevyengine#18105 includes a check for running headless, but this allows for an extra world update during shutdown. This commit checks if the `AppExit` event has been recorded and prevents the additional world update. ### Before ``` 2025-03-06T03:11:59.999679Z INFO bevy_window::system: No windows are open, exiting 2025-03-06T03:12:00.001942Z INFO bevy_winit::system: Closing window 0v1 2025-03-06T03:12:00.012691Z INFO bevy_window::system: No windows are open, exiting ``` ### After ``` 2025-03-06T03:18:45.552243Z INFO bevy_window::system: No windows are open, exiting 2025-03-06T03:18:45.554119Z INFO bevy_winit::system: Closing window 0v1 ``` ## Testing Ran `window` examples - `monitor_info` continues to run after all windows are closed (it has `ExitCondition::DontExit`) - `window_settings` invisible window creation works as expected - `multiple_windows` exits after both windows are closed with a single exit message
…e to update the pr (bevyengine#18153) # Objective - When a PR gets merged that modifies the rendering screenshots, the main reference will be updated - Every in-flight PR will then "fail" rendering change detection as they come from an outdated main branch ## Solution - Suggest updating the PR to the latest main branch
## Objective Fixes bevyengine#18092 Bevy's current error type is a simple type alias for `Box<dyn Error + Send + Sync + 'static>`. This largely works as a catch-all error, but it is missing a critical feature: the ability to capture a backtrace at the point that the error occurs. The best way to do this is `anyhow`-style error handling: a new error type that takes advantage of the fact that the `?` `From` conversion happens "inline" to capture the backtrace at the point of the error. ## Solution This PR adds a new `BevyError` type (replacing our old `std::error::Error` type alias), which uses the "from conversion backtrace capture" approach: ```rust fn oh_no() -> Result<(), BevyError> { // this fails with Rust's built in ParseIntError, which // is converted into the catch-all BevyError type let number: usize = "hi".parse()?; println!("parsed {number}"); Ok(()) } ``` This also updates our exported `Result` type alias to default to `BevyError`, meaning you can write this instead: ```rust fn oh_no() -> Result { let number: usize = "hi".parse()?; println!("parsed {number}"); Ok(()) } ``` When a BevyError is encountered in a system, it will use Bevy's default system error handler (which panics by default). BevyError does custom "backtrace filtering" by default, meaning we can cut out the _massive_ amount of "rust internals", "async executor internals", and "bevy system scheduler internals" that show up in backtraces. It also trims out the first generally-unnecssary `From` conversion backtrace lines that make it harder to locate the real error location. The result is a blissfully simple backtrace by default:  The full backtrace can be shown by setting the `BEVY_BACKTRACE=full` environment variable. Non-BevyError panics still use the default Rust backtrace behavior. One issue that prevented the truly noise-free backtrace during panics that you see above is that Rust's default panic handler will print the unfiltered (and largely unhelpful real-panic-point) backtrace by default, in _addition_ to our filtered BevyError backtrace (with the helpful backtrace origin) that we capture and print. To resolve this, I have extended Bevy's existing PanicHandlerPlugin to wrap the default panic handler. If we panic from the result of a BevyError, we will skip the default "print full backtrace" panic handler. This behavior can be enabled and disabled using the new `error_panic_hook` cargo feature in `bevy_app` (which is enabled by default). One downside to _not_ using `Box<dyn Error>` directly is that we can no longer take advantage of the built-in `Into` impl for strings to errors. To resolve this, I have added the following: ```rust // Before Err("some error")? // After Err(BevyError::message("some error"))? ``` We can discuss adding shorthand methods or macros for this (similar to anyhow's `anyhow!("some error")` macro), but I'd prefer to discuss that later. I have also added the following extension method: ```rust // Before some_option.ok_or("some error")?; // After some_option.ok_or_message("some error")?; ``` I've also moved all of our existing error infrastructure from `bevy_ecs::result` to `bevy_ecs::error`, as I think that is the better home for it ## Why not anyhow (or eyre)? The biggest reason is that `anyhow` needs to be a "generically useful error type", whereas Bevy is a much narrower scope. By using our own error, we can be significantly more opinionated. For example, anyhow doesn't do the extensive (and invasive) backtrace filtering that BevyError does because it can't operate on Bevy-specific context, and needs to be generically useful. Bevy also has a lot of operational context (ex: system info) that could be useful to attach to errors. If we have control over the error type, we can add whatever context we want to in a structured way. This could be increasingly useful as we add more visual / interactive error handling tools and editor integrations. Additionally, the core approach used is simple and requires almost no code. anyhow clocks in at ~2500 lines of code, but the impl here uses 160. We are able to boil this down to exactly what we need, and by doing so we improve our compile times and the understandability of our code.
…gine#18178) # Objective I'm building a bloxel game in which I (currently) use a texture atlas to render the blocks the world is made of. While I was coding it, I was using the `TextureAtlas...` types to build the terrain's texture atlas at runtime as shown in the [example](https://github.com/bevyengine/bevy/blob/latest/examples/2d/texture_atlas.rs). But when I was using it to build a 3D mesh out of the blocks, I found that there was no easy way get the texture rect in UV coordinates, only in pixels via `texture_rect()`. I had to resort to writing code like this: ```rs let size = layout.size.as_vec2(); if let Some(rect) = sources.texture_rect(layout, texture) { let rect = rect.as_rect(); let uvs = Rect::from_corners(rect.min / size, rect.max / size); // use the UVs here, such as to build vertex buffer } ``` That is, until I wrote a helper function that's practically identical to the one in this PR. ## Solution Add a `uv_rect` function to `TextureAtlasSources` that will return a `Rect` with coordinates in the range of 0.0 to 1.0 – that is, UV coordinates – which can then be used directly to build `Vec2` UV values to put into a buffer and send to the GPU. I'm a little unsure about the wording of the `texture_rect` documentation but I kept it intact and based mine on it. If you think this could be improved and have some advice, I'd love to include that in this PR. ## Testing I've not done any testing with the updated bevy branch, other than seeing that the original helper function (identical in functionality) worked in my currently very small project, and making sure `cargo build` doesn't error, but I'm new to making changes to Bevy so unsure if this is sufficient. ## Showcase 
# Objective simplify some code and improve Event macro Closes bevyengine#14336, # Showcase you can now write derive Events like so ```rust #[derive(event)] #[event(auto_propagate, traversal = MyType)] struct MyEvent; ```
# Objective Alternative to and closes bevyengine#18120. Sibling to bevyengine#18082, see that PR for broader reasoning. Folks weren't sold on the name `many` (get_many is clearer, and this is rare), and that PR is much more complex. ## Solution - Simply deprecate `Query::many` and `Query::many_mut` - Clean up internal usages Mentions of this in the docs can wait until it's fully removed in the 0.17 cycle IMO: it's much easier to catch the problems when doing that. ## Testing CI! ## Migration Guide `Query::many` and `Query::many_mut` have been deprecated to reduce panics and API duplication. Use `Query::get_many` and `Query::get_many_mut` instead, and handle the `Result`. --------- Co-authored-by: Chris Russell <[email protected]>
# Objective - Fixes bevyengine#15460 (will open new issues for further `no_std` efforts) - Supersedes bevyengine#17715 ## Solution - Threaded in new features as required - Made certain crates optional but default enabled - Removed `compile-check-no-std` from internal `ci` tool since GitHub CI can now simply check `bevy` itself now - Added CI task to check `bevy` on `thumbv6m-none-eabi` to ensure `portable-atomic` support is still valid [^1] [^1]: This may be controversial, since it could be interpreted as implying Bevy will maintain support for `thumbv6m-none-eabi` going forward. In reality, just like `x86_64-unknown-none`, this is a [canary](https://en.wiktionary.org/wiki/canary_in_a_coal_mine) target to make it clear when `portable-atomic` no longer works as intended (fixing atomic support on atomically challenged platforms). If a PR comes through and makes supporting this class of platforms impossible, then this CI task can be removed. I however wager this won't be a problem. ## Testing - CI --- ## Release Notes Bevy now has support for `no_std` directly from the `bevy` crate. Users can disable default features and enable a new `default_no_std` feature instead, allowing `bevy` to be used in `no_std` applications and libraries. ```toml # Bevy for `no_std` platforms bevy = { version = "0.16", default-features = false, features = ["default_no_std"] } ``` `default_no_std` enables certain required features, such as `libm` and `critical-section`, and as many optional crates as possible (currently just `bevy_state`). For atomically-challenged platforms such as the Raspberry Pi Pico, `portable-atomic` will be used automatically. For library authors, we recommend depending on `bevy` with `default-features = false` to allow `std` and `no_std` users to both depend on your crate. Here are some recommended features a library crate may want to expose: ```toml [features] # Most users will be on a platform which has `std` and can use the more-powerful `async_executor`. default = ["std", "async_executor"] # Features for typical platforms. std = ["bevy/std"] async_executor = ["bevy/async_executor"] # Features for `no_std` platforms. libm = ["bevy/libm"] critical-section = ["bevy/critical-section"] [dependencies] # We disable default features to ensure we don't accidentally enable `std` on `no_std` targets, for example. bevy = { version = "0.16", default-features = false } ``` While this is verbose, it gives the maximum control to end-users to decide how they wish to use Bevy on their platform. We encourage library authors to experiment with `no_std` support. For libraries relying exclusively on `bevy` and no other dependencies, it may be as simple as adding `#![no_std]` to your `lib.rs` and exposing features as above! Bevy can also provide many `std` types, such as `HashMap`, `Mutex`, and `Instant` on all platforms. See `bevy::platform_support` for details on what's available out of the box! ## Migration Guide - If you were previously relying on `bevy` with default features disabled, you may need to enable the `std` and `async_executor` features. - `bevy_reflect` has had its `bevy` feature removed. If you were relying on this feature, simply enable `smallvec` and `smol_str` instead. --------- Co-authored-by: Alice Cecile <[email protected]>
…yengine#17992) Fixes bevyengine#17170 # Objective Tangents are not currently transformed as described in bevyengine#17170. I came across this while working on bevyengine#17989 and it seemed like low hanging fruit.
…component-access # Conflicts: # crates/bevy_ecs/src/schedule/executor/mod.rs # crates/bevy_ecs/src/schedule/executor/multi_threaded.rs # crates/bevy_ecs/src/system/observer_system.rs # crates/bevy_ecs/src/system/schedule_system.rs
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Original PR: bevyengine#16885
This is a test to see how Codspeed reacts to benchmarking pull requests.