# Objective
Remove the `new_with_` prefixes from the `TextLayout` constuctor
functions. Generally, the "new" part is redundant and "with" is used by
fluent APIs.
## Solution
Just delete the prefixes, shorten the names (all on `TextLayout`).
* `new_with_justify` -> `justify`
* `new_with_linebreak` -> `linebreak`
* `new_with_no_wrap` -> `no_wrap`
# Objective
- In 0.18, we had 10 different functions that load assets (I'm not even
counting `load_folder`).
- In 0.19, we've even added `load_erased` - but it unfortunately doesn't
support all the features that the other variants support.
- We apparently needed `load_acquire_with_settings_override` which 1)
loads the asset, 2) uses the settings provided, 3) allows reading
unapproved asset paths, and 4) drops a guard once the load completes.
- That's fine if that's necessary. But we needed to create an explicit
variant for that.
- We need fewer load paths!
## Solution
- Create a builder.
- Store all these options dynamically instead of statically handling
each case.
- Have the caller choose a particular "kind" of load when they are
ready: `load`, `load_erased`, `load_untyped`, or `load_untyped_async`.
- I intentionally didn't provide a `load_async` or `load_erased_async`,
since those can be replicated using `load`/`load_erased` +
`AssetServer::wait_for_asset_id` to get the exact same effect.
I am also intentionally leaving `NestedLoader` untouched in this PR, but
a followup will duplicate this API for `NestedLoader`, which should make
it easier to understand.
Unlike the `NestedLoader` API, we aren't doing any type-state craziness,
so the docs are much more clear: users don't need to understand how
type-state stuff works, they just call the handful of methods on the
type. The "cost" here is we now need to be careful about including the
cross product of loads between static asset type, runtime asset type, or
dynamic asset type, crossed with deferred or async. In theory, if we
added more kinds on either side, we would need to expand this cross
product a lot. In practice though, it seems unlikely there will be any
more variants there. (maybe there could be a blocking variant? I don't
think this is a popular opinion though).
A big con here is some somewhat common calls are now more verbose.
Specifically, `asset_server.load_with_settings()` has become
`asset_server.load_builder().with_settings().load()`. I am not really
concerned about this though, since it really isn't that painful.
## Testing
- Tests all pass!
---
## Showcase
Now instead of:
```rust
asset_server.load_acquire_with_settings_override("some_path", |settings: &mut GltfLoaderSettings| { ... }, my_lock_guard);
```
You can instead do:
```rust
asset_server.load_builder()
.with_guard(my_lock_guard)
.with_settings(|settings: &mut GltfLoaderSettings| { ... })
.override_unapproved()
.load("some_path");
```
We also now cover more variants! For example, you can now load an asset
untyped with a guard, or with override_unapproved, etc.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- Clean up our texture format handling.
- Fix#23732
- Get us closer to #22563
- It makes no sense for views to talk about being hdr or not. They have
a texture format, that's it. What does HDR shadows even mean lol
- Same for compositing_space
## Solution
- Remove ExtractedView::hdr
- Add ExtractedView::texture_format
- Move ExtractedView::compositing_space to
ExtractedCamera::compositing_space
- Add texture_format to a bunch of specialization keys instead of hdr
bool
- Convert VolumetricFogPipelineKey to not use flags and just use bool
and texture format
- Remove BevyDefault TextureFormat
- Remove ViewTarget TEXTURE_FORMAT_HDR
## Testing
- Pretty extensively test at this point
This has a migration guide.
---------
Co-authored-by: Willow Black <wmcblack@gmail.com>
Co-authored-by: Máté Homolya <mate.homolya@gmail.com>
Co-authored-by: Luo Zhihao <luo_zhihao@outlook.com>
Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
# Objective
Fixes#22863. Fixes#22999. Fixes#23142.
## Solution
1. Re-add `Out` type to `ExtractComponent`. Rename `SyncComponent::Out`
to `SyncComponent::Target`, which is used to clean up components when
removing, so that the output of extraction can be different from
`SyncComponent::Target`.
2. Add `#[extract_component_sync_target]` attribute for
`#[derive(ExtractComponent)]` to specify `SyncComponent::Target`.
## Testing
Tested `ssr` and `order_independent_transparency` examples.
There may be other places where derived components need to be cleaned
up, but finding them is somewhat challenging.
wgpu update for v29.
I have tested on macos m1, m5, and windows. Linux testing is
appreciated.
- [x] before merge, naga_oil and dlss_wgpu need to be published, and the
patches referencing their respective PRs removed from the workspace
Cargo.toml
##### other PRs
- naga_oil: https://github.com/bevyengine/naga_oil/pull/134
- dlss_wgpu: https://github.com/bevyengine/dlss_wgpu/pull/27
##### Source of relevant changes
- `Dx12Compiler::DynamicDxc` no longer has `max_shader_model`
- https://github.com/gfx-rs/wgpu/pull/8607
- `Dx12BackendOptions::force_shader_model` comes from:
- https://github.com/gfx-rs/wgpu/pull/8984
- Allow optional `RawDisplayHandle` in `InstanceDescriptor`
- https://github.com/gfx-rs/wgpu/pull/8012
- Add `GlDebugFns` option to disable OpenGL debug functions
- https://github.com/gfx-rs/wgpu/pull/8931
- Add a DX12 backend option to force a certain shader model
- https://github.com/gfx-rs/wgpu/pull/8984
- Migrate validation from maxInterStageShaderComponents to
maxInterStageShaderVariables
- https://github.com/gfx-rs/wgpu/pull/8652
- gaps are now supported in bind group layouts
- https://github.com/gfx-rs/wgpu/pull/9034
- depth validation changed to option to match spec
- https://github.com/gfx-rs/wgpu/pull/8840
- SHADER_PRIMITIVE_INDEX is now PRIMITIVE_INDEX
- https://github.com/gfx-rs/wgpu/pull/9101
- Support for binding arrays of RT acceleration structures
- https://github.com/gfx-rs/wgpu/pull/8923
- Make HasDisplayHandle optional in WindowHandle
- https://github.com/gfx-rs/wgpu/pull/8782
- `QueueWriteBufferView` can no longer be dereferenced to `&mut [u8]`,
so use `WriteOnly`.
- https://github.com/gfx-rs/wgpu/pull/9042
- ~bevy_mesh currently has an added dependency on `wgpu`, can we move
`WriteOnly` to wgpu-types?~ (it is in wgpu-types now)
- Change max_*_buffer_binding_size type to match WebGPU spec (u32 ->
u64)
- https://github.com/gfx-rs/wgpu/pull/9146
- raw vulkan init `open_with_callback` takes Limits as argument now
- https://github.com/gfx-rs/wgpu/pull/8756
## Known Issues
There is currently one known issue with occlusion culling on macos,
which we've decided to disable on macos by checking the limits we
actually require. This makes it so that if wgpu releases a patch fix,
bevy 0.19 users will benefit from occlusion culling re-enabling for
them.
<details><summary>More details</summary>
On macos, the wpgu limits were changed to align with the spec and now
put the early and late GPU occlusion culling `StorageBuffer` limit at 8,
but we currently use 9. [Filed in wgpu
repo](https://github.com/gfx-rs/wgpu/issues/9287)
```
2026-03-19T01:37:10.771117Z ERROR bevy_render::error_handler: Caught rendering error: Validation Error
Caused by:
In Device::create_bind_group_layout, label = 'build mesh uniforms GPU late occlusion culling bind group layout'
Too many bindings of type StorageBuffers in Stage ShaderStages(COMPUTE), limit is 8, count was 9. Check the limit `max_storage_buffers_per_shader_stage` passed to `Adapter::request_device`
```
</details>
solari working on wgpu 29:
<img width="1282" height="752" alt="image"
src="https://github.com/user-attachments/assets/4744faec-65c0-4a72-93e1-34a721fc26d8"
/>
---------
Co-authored-by: atlv <email@atlasdostal.com>
# Objective
Depends on #22187. Fixes#17794. ~For platform consistency I think it’s
reasonable to enable primitive restart by default.~ wgpu will force
primitive restart after https://github.com/gfx-rs/wgpu/pull/8850.
## Solution
Add index format to MeshPipelineKey, replace
`MeshPipelineKey::from_primitive_topology` with
`MeshPipelineKey::from_primitive_topology_and_index`, and enable
`strip_index_format` in render pipeline.
## Testing
I modified the `lines` example to demonstrate primitive restart.
## Showcase
<details>
<summary>Click to view showcase</summary>
<img width="1550" height="852" alt="屏幕截图_20251218_210849"
src="https://github.com/user-attachments/assets/a7c41943-f22b-415a-8132-98455f21735d"
/>
</details>
Currently, Bevy handles meshes tagged with `NoCpuCulling` by simply
always adding them to `VisibleEntities`. This is, however, inefficient,
because `VisibleEntities` has to be repopulated with the entity IDs of
such meshes every frame and copied to the render world. When scaling to
millions of entities, this becomes a significant bottleneck.
This PR changes the visibility systems to ignore meshes with
`NoCpuCulling` entirely. Instead of being added to `VisibleEntities`,
the mesh extraction systems instead use standard ECS queries to iterate
over meshes with `NoCpuCulling` directly, in addition to any entities in
`VisibleEntities` that use CPU culling. For efficiency,
`RenderVisibleMeshEntities` now tracks mesh instances that are subject
to CPU culling and those that opted out of CPU culling in two separate
data structures. Note that this required changing the signatures of
`DirtySpecializations` methods to return a tuple of references instead
of a reference to a tuple. Although that change looks complicated, it's
actually just reshuffling to accommodate this slight type change, not a
change in logic.
On `bevy_city`, `check_visibility` takes a median of 1.19 ms, and
`check_dir_light_mesh_visibility` takes a median of 4.33 ms. With this
patch, these systems entirely disappear if `NoCpuCulling` is added to
every mesh.
<img width="2756" height="1800" alt="Screenshot 2026-02-22 020929"
src="https://github.com/user-attachments/assets/18048399-bcfd-4165-8491-8d126d73534e"
/>
# Objective
- Rename the `examples/games` directory to `examples/showcase`. The
"showcase" category is used for examples that are more comprehensive and
full-featured than most, but which are not necessarily games.
# Objective
- Resolve most of the warnings in the Build Docs step of Deploy Docs,
you can see them here:
https://github.com/bevyengine/bevy/actions/runs/23021953246/job/66860356132
## Solution
- Resolve most of the warnings
- The `doc_cfg` feature should only be enabled with `docsrs`, not
`docsrs_dep` (I just followed this pattern from other crates tbh like
`bevy_math` and `bevy_material`)
- I unlinked example docs that references non public items within the
example itself
- I corrected some links
Note: I didn’t fix the warnings concerning the macros in bevy-reflect
for `tuple.rs` because I’m not macro savvy. If someone knows what to do
in those cases (should I just remove the `$(#[$meta])*` lines cause
they’re not in use?), just let me know and I can do it (or you can open
a pull!)
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Fixes#22888
## Solution
- Add an `orientation` field to `TileData`. This contains a
`TileOrientation` enum representing the tile being rotated and/or
mirrored. This defaults to the current orientation (tile is not rotated
or mirrored). The orientation is packed into the `flags` of
`PackedTileData` as 3 new bits alongside the existing `visible` boolean.
The bits represent mirroring horizontally (left and right swapped),
mirroring vertically (top and bottom swapped), and diagonally (top-right
and bottom-left corners are swapped). Together, the 8 combinations
represent all possible combinations of 90 degree rotations with
mirroring in the X or Y axis.
- Update `tilemap_chunk_material.wgsl` to unpack the bits, and use them
to modify the `local_uv` as needed to apply the transform to the tile.
`TileOrientation` also provides some convenience methods for
construction, and for treating the orientation as a transform that can
be inverted and combined, as well as applying that transform to an
`IVec2`.
There is a new `tilemap_chunk_orientation` example showing all
orientations, combined with tile indices, color, alpha and visibility to
check packing of data.
## Testing
There are some simple tests on the `TileOrientation` itself (e.g.
checking that combining an orientation with its inverse produces the
default orientation, applying each orientation as a transform to a test
`IVec2` to confirm the result is as expected based on manually worked
out expected results, and that inverses will map a point back to its
origin).
Rendering can be tested using the new example, I've tested this on a
Macbook M1 Max.
---
## Showcase
The new `tilemap_chunk_orientation` example uses one tileset entry for
each tile on each row, showing the 8 transforms along the row:
<img width="687" height="707" alt="Screenshot 2026-02-23 at 14 25 29"
src="https://github.com/user-attachments/assets/9cb20236-d180-490c-bd3f-f6e61c30c099"
/>
This also shows different colors and alpha values on each of 8 rows to
check they work alongside transforms, the top row is set to invisible.
The `tileset_index` alternates per row.
This uses a simple two-tile tileset, shown below scaled up by 4:
<img width="32" height="64" alt="arrow"
src="https://github.com/user-attachments/assets/5536beb9-7a3e-4ec0-a667-777ba8ad283a"
/>
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Right now, every frame, all specialization and queuing systems iterate
over all entities visible from a view and check to see whether they need
to be updated by consulting a set of change ticks and comparing them to
the current change ticks. To handle cases in which a mesh needs to be
removed from the bins, a separate final *sweep* pass then finds entities
that no longer exist and removes them manually from the bins. This
process is complex, error-prone, and slow, as it involves visiting all
visible entities multiple times every frame.
This PR changes the setup so that, instead of examining change ticks,
the visibility logic pushes the set of added and removed entities to
each view explicitly. The visibility system determines which meshes need
to be added and removed by first sorting the list of visible entities,
then performing an O(n) diff process on the last frame's visible
entities and this frame's visible entity list. The end result is that
the specialization and queuing systems only process the entities that
they need to every frame. If a mesh was visible last frame, remained
visible this frame, and didn't change its mesh or material, then it's
generally not examined at all. Not only is this significantly faster for
virtually all realistic scenes, but it's also much simpler.
In order to achieve the benefits of not examining every visible mesh
every frame, I made sorted render passes retained via an `IndexMap`.
This allows entities to be removed and added via random access while
still allowing the list to be sorted by distance. Note that I had to
remove the radix sort because `IndexMap` doesn't currently support that;
I believe the enormous speed benefits of this patch outweigh any minor
sorting regressions from this.
I tested this PR by running `scene_viewer` on a test scene with many
meshes and materials and implementing a material shuffler that randomly
switches the materials around. I tested the following cases:
* Moving the camera so that meshes become visible and invisible.
* Switching opaque materials on meshes.
* Moving meshes from opaque to alpha masked and vice versa.
* Moving meshes from binned render passes to sorted render passes (i.e.
transparent).
* All of the above while the meshes were off screen, then moving them on
screen to ensure that the changes took effect.
This PR brings the `specialize_shadows` time on the `bevy_city` demo
from 12.87 ms per frame to 0.1261 ms per frame, a 102x speedup. It
brings the `queue_shadows` time on the same demo from 12.34 ms per frame
to 0.1102 ms, a 111x speedup. Mean frame time goes from 50.16 ms to
23.26 ms, a 2.16x speedup.
`specialize_shadows` in `bevy_city` before and after:
<img width="2756" height="1800" alt="Screenshot 2026-02-14 180313"
src="https://github.com/user-attachments/assets/dbc3c68b-e0ec-424f-8085-87c0f5f41d3f"
/>
`queue_shadows` in `bevy_city` before and after:
<img width="2756" height="1800" alt="Screenshot 2026-02-14 180500"
src="https://github.com/user-attachments/assets/08f8e1bb-6ab4-47da-ae68-a80156d59caa"
/>
Frame graph of `bevy_city` before:
<img width="2756" height="1800" alt="Screenshot 2026-02-12 203324"
src="https://github.com/user-attachments/assets/d0807cee-23a2-4e14-be1a-7466b795ebfa"
/>
Frame graph of `bevy_city` after:
<img width="2756" height="1800" alt="Screenshot 2026-02-14 180506"
src="https://github.com/user-attachments/assets/b22acf0f-a6f9-432b-93d7-f8057c815b05"
/>
# Objective
- `glam`, `hexasphere` & `rand` have released their latest versions,
update Bevy to support them.
## Solution
- The above have been updated to their compatible versions. `rand_distr`
updated as well to match `rand` v0.10 support.
- `rand_chacha` is soft deprecated and no longer used by `rand`, so its
usage has been changed to `chacha20` to match `rand` dep tree.
- `uuid` is in the process of updating to `getrandom` v0.4, which `rand`
v0.10 supports. This PR remains in draft until a new `uuid` release hits
crates.io.
- `RngCore` is now `Rng`, and `Rng` is now `RngExt`, so this required
updating across many files.
- `choose_multiple` method is deprecated, changed to `sample`.
## Testing
- Chase all compiler errors, since this should not regress any already
existing behaviour.
- This must pass CI without regressions.
## Additional Notes
`getrandom` v0.4 doesn't add anything new for Web WASM support, so the
same `wasm_js` feature is used.
# Objective
- Fixes#16751
## Solution
- `Assets::get_mut` now returns a wrapper `AssetMut` type instead of
`&mut impl Asset`.
- `AssetMut` implements `Deref` and `DerefMut`.
- `DerefMut` marks assets as changed.
- when dropped `AssetMut` will add `AssetEvent::Modified` event to a
queue only in case asset was marked as changed.
## Testing
- Did you test these changes? If so, how?
- No unit tests were added, change is pretty straightforward.
- Test project: https://github.com/MatrixDev/bevy-feature-16751-test.
- With change: ~100 fps.
- Without change: ~15 fps.
- Are there any parts that need more testing?
- I don't really see how this can break anything or add a measurable
overhead.
- `AssetEvent::Modified` will now be sent after the asset was modified
instead of before. It should not affect anything but still worth noting.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Have a big amount of entities that constantly update their materials.
- Properties of those materials should be animated in a stepped maned
(like changing color every 0.1 seconds).
- Update material only if value has actually changed:
```rust
if material.base_color != new_color {
material.base_color = new_color;
}
```
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
- tested on macos (Mackbook M1)
- not a platform-specific issue
PS: This is my first PR, so please don’t judge.
# Objective
Fixes#18722, and allows `ExtractComponent` to be used for foreign
types.
## Solution
* Split the `Out` type from `ExtractComponent` to a `SyncComponent`
trait. This allows types to use the synchronization logic without the
extraction logic, and allows `SyncComponentPlugin` to correctly identify
which components should be removed.
* Don't delete the entire entity but only the `Out` components in
`SyncComponentPlugin`/`SyncWorldPlugin`, fixing #18722.
* Add marker types to `ExtractComponent` and `SyncComponent`, allowing
them to be implemented for foreign types outside `bevy_render`.
(Example: `DirectionalLight` is defined in `bevy_light` which doesn't
depend on `bevy_render`, and used by `bevy_pbr`. Without the marker no
crate is allowed to implement the trait.)
During some earlier render crate refactors by @atlv24, some uses of
`ExtractComponent` was converted to manual implementations. I have not
ported these back, that can be done in follow up PRs.
As a follow up it might be interesting to make a derive macro for
`SyncComponent`, and/or update the `ExtractComponent` macro to be able
to customize the behavior around syncing.
## Testing
Ran a bunch of the examples. It would be good to test others, especially
ones that toggle components.
~~A test case is in #22758. If that one gets merged first this PR should
be updated to uncomment the relevant assert.~~ edit: the assert has been
added.
# Objective
- Recover from rendering errors.
- Another step towards render recovery after #22714#22759 and #16481
## Solution
- Use `wgpu::Device::set_device_lost_callback` and
`wgpu::Device::on_uncaptured_error` to listen for errors.
- Add a state machine for the renderer
- Update it on error
- Add a `RenderErrorHandler` to let users specify behavior on error by
returning a specific `RenderErrorPolicy`
- This lets us for example ignore validation errors, delete responsible
entities, or reload the renderer if the device was lost.
## Testing
- #22757 with any of
```rs
.insert_resource(bevy_render::error_handler::RenderErrorHandler(|_, _, _| {
bevy_render::error_handler::RenderErrorPolicy::StopRendering
}))
```
```rs
.insert_resource(bevy_render::error_handler::RenderErrorHandler(|_, _, _| {
bevy_render::error_handler::RenderErrorPolicy::Recover(default())
}))
```
Note: no release note yet, as recovery does not exactly work well: this
PR gets us to the point of being able to care about it, but we currently
instantly crash on recover due to gpu resources not existing anymore. We
need to build more resilience before publicizing imo.
---------
Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
# 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>
# render-graph-as-systems
> [!NOTE]
> Remember to check hide whitespace in diff view options when reviewing
this PR
This PR removes the `RenderGraph` in favor of using systems.
## Motivation
The `RenderGraph` API was originally created when the ECS was
significantly more immature. It was also created with the intention of
supporting an input/output based slot system for managing resources that
has never been used. While resource management is an important potential
use of a render graph, current rendering code doesn't make use of any
patterns relating to it.
Since the ECS has improved, the functionality of `Schedule` has
basically become co-extensive with what the `RenderGraph` API is doing,
i.e. ordering bits of system-like logic relative to one another and
executing them in a big chunk. Additionally, while there's still desire
for more advanced techniques like resource management in the graph, it's
desirable to implement those in ECS terms rather than creating more
`RenderGraph` specific abstraction.
In short, this sets us up to iterate on a more ECS based approach, while
deleting ~3k lines of mostly unused code.
## Implementation
At a high level: We use `Schedule` as our "sub-graph." Rather than
running the graph, we run a schedule. Systems can be ordered relative to
one another.
The render system uses a `RenderGraph` schedule to define the "root" of
the graph. `core_pipeline` adds a `camera_driver` system that runs the
per-camera schedules. This top level schedule provides an extension
point for apps that may want to do custom rendering, or non-camera
rendering.
### `CurrentView` / `ViewQuery`
When running schedules per-camera in the `camera_driver` system, we
insert a `CurrentView` resource that's used to mark the currently
iterating view. We also add a new param `ViewQuery` that internally uses
this resource to execute the query and skip the system if it doesn't
match as a convenience.
### `RenderContext`
The `RenderContext` is now a system param that wraps a `Deferred` for
tracking the state of the current command encoder and queued buffers.
### `SystemBuffer`
We use an system buffer impl to track command encoders in the render
context and rely on apply deferred in order to encode them all.
Currently, this encodes them in series. There are likely opportunities
here to make this more efficient.
## Benchmarks
### Bistro
<img width="1635" height="825" alt="Screenshot 2026-01-15 at 7 57 40 PM"
src="https://github.com/user-attachments/assets/8e55a959-89a3-4947-bfc5-c04780f82e7b"
/>
### Caldera
<img width="1631" height="828" alt="Screenshot 2026-01-15 at 8 13 06 PM"
src="https://github.com/user-attachments/assets/e7e8ae0d-41c3-430f-8b4d-9099b3d922a0"
/>
## Future steps
There are a number of exciting potential changes that could follow here:
- We can explore adding something like a read-only schedule to pick up
some more potential parallelism in graph execution.
- We can use more things like run conditions in order to prevent systems
from running at all in the first place.
- We can explore things like automating resource creation via system
params.
## TODO:
- [x] Make sure 100% of everything still works.
- [x] Benchmark to make sure we don't regress performance
- [x] Re-add docs
---------
Co-authored-by: atlas dostal <rodol@rivalrebels.com>
# Objective
Adopt and closes#22665
## Solution
Delete bevy's `Affine3`, create an extension trait for methods create
for old bevy's `Affine3` to be used by glam's `Affine3`, and register
glam's `Affine3` for reflection
## Testing
`cargo run -p ci`
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
Implement support for the remaining missing text features with minimal
changes.
## Solution
`TextFont` has been expanded to include new fields:
```rust
pub struct TextFont {
pub font: FontSource,
pub font_size: f32,
pub weight: FontWeight,
pub width: FontWidth,
pub style: FontStyle,
pub font_smoothing: FontSmoothing,
pub font_features: FontFeatures,
}
```
FontSource has two variants: Handle, which identifies a font by asset
handle, and Family, which selects a font by its family name.
`FontWidth` is a newtype struct representing OpenType font stretch
classifications ranging from ULTRA_CONDENSED (50%) to ULTRA_EXPANDED
(200%).
`FontStyle` is an enum used to set the slant style of a font, either
`Normal`, `Italic`, or `Oblique`.
The system font support is very barebones. You load them using the
`CosmicFontSystem` resource:
```rust
font_system.db_mut().load_system_fonts()
```
Then they are available to be selected by family name using
`FontSource::Family`.
### Other changes
* `TextPipelines`'s `glyph_info` field has been removed. There is no
need to collect the section infos or perform any querys during text
layout updates, so that code has been removed as well.
* `update_text_layout_info` used some `try_for_each` with some nested
closures which was unnecessarily complicated again. They've been
replaced with a regular for loop.
* After font assets are loaded there's a new system
`load_font_assets_into_fontdb_system` that automatically adds them to
cosmic text's font database. Then they are available to be looked up by
family name as well as by asset handle.
* There aren't are performance motivated changes but layout updates seem
to be overall significantly more efficient now, with a slight regression
for very large numbers of short, single section text entities.
* Font texture atlases are no longer automatically cleared when the font
asset they were generated from is removed. There is no way to remove
individual fonts from cosmic text's `FontSystem`, so the font is still
accessible using the family name with `FontSource::family` and removing
the text atlases naively could cause a panic since rendering expects
them to be present.
## Testing
```
cargo run --example font_query
```
---
## Showcase
<img width="1229" height="591" alt="font-query"
src="https://github.com/user-attachments/assets/23f5aaa2-fdb8-4448-9b4e-9d65d6431107"
/>
---------
Co-authored-by: Thierry Berger <contact@thierryberger.com>
The [AMD FidelityFX single-pass downsampler] (SPD) is the fastest way to
generate mipmap levels of a texture. Bevy currently has two separate
ports of that algorithm to WGSL: one for use in the environment map
generation and one for use on the depth buffer for the purposes of
occlusion culling (though the latter isn't the best use of it). Absent
is any mechanism to use the single-pass downsampler to generate mipmap
levels of a color texture for typical use in rendering. This is a
standard feature in game engines: for example, Unity has
[`GenerateMips`] and Unreal has [`bAutoGenerateMips`].
This PR adds a mechanism by which applications can invoke SPD to
generate mipmap levels for any `Image`. Using this mechanism is a two
step process. First, the application adds the `Handle<Image>` to a
resource, `MipGenerationJobs` and associates it with a *phase*, which is
an arbitrary ID chosen by the application. Second, the application adds
a `MipGenerationNode` for that phase to the render graph. During
rendering, the `MipGenerationNode` invokes SPD to generate a full mipmap
chain for all textures in that phase.
The reason why mipmap generation jobs are associated with phases is that
the generation of mipmaps may need to occur at precise points in the
application rendering cycle. For example, consider the common situation
of a mipmapped portal texture. The mipmaps must be generated *after* the
portal is rendered, but *before* the object in the main world displaying
the portal texture is drawn. The phased approach taken in this PR allows
complex dependencies like this to be expressed using the node graph
feature that Bevy already possesses. (In the future, if render graphs
are removed in favor of systems, this approach can naturally be reframed
in terms of systems, so this patch contains no hazards in that regard.)
Note that this patch by itself doesn't automatically generate mipmaps
for imported textures that don't have them the way that
[`bevy_mod_mipmap_generator`] does, in order to keep this patch
relatively small and self-contained. However, it'd be straightforward to
either (a) extend `bevy_mod_mipmap_generator`, (b) write another plugin,
and/or (c) add a new feature to Bevy itself, all built on top of this
PR, to support automatic GPU mip generation for image assets that don't
have them.
A new example, `dynamic_mip_generation`, has been added. This is a 2D
example that produces a texture at runtime on the CPU and invokes the
new `MipGenerationNode` that this patch adds to generate mipmaps for
that texture at runtime. The colors of the texture are randomly
generated, and UI for the example allows the texture to be regenerated
and for the size to be adjusted; this proves that the mipmap levels for
the texture are indeed generated at runtime and not pre-calculated at
build time. Note that, although the example is 2D, the feature that this
patch adds can be equally used in 2D and 3D.
[AMD FidelityFX single-pass downsampler]:
https://gpuopen.com/fidelityfx-spd/
[`GenerateMips`]:
https://docs.unity3d.com/ScriptReference/Rendering.CommandBuffer.GenerateMips.html
[`bAutoGenerateMips`]:
https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/DisplayClusterConfiguration/FDisplayClusterC-_33/bAutoGenerateMips
[`bevy_mod_mipmap_generator`]:
https://github.com/DGriffin91/bevy_mod_mipmap_generator
# Objective
- Fixes#22126
## Solution
- Use the correct import in the example shader
## Testing
- Tested on Linux with `cargo run --example mesh2d_manual`
# Objective
#20830 created the possibility that we may want to have render targets
that produce a number of outputs, e.g. depth and normals. This is a
first step towards something like that (e.g. a `RendersTo` relation) by
converting `RenderTarget` to be a component. This is also useful for
out-of-tree render targets that may want to do something like
`#[require(RenderTarget::Image)]` once BSN lands.
## Solution
Make it a component.
# Objective
If you want to use a `TilemapChunk` (or more generally use a
`texture2DArray` in a shader), you have to implement a mechanism that
waits for your texture to load, then calls
`Image::reinterpret_stacked_2d_as_array`.
## Solution
Have the loader do it instead.
Closes#20799, which does very similar things and should be remade if
more functionality is needed.
## Testing
- Ran the updated examples
---
## Showcase
```rs
let array_texture = asset_server.load_with_settings(
"textures/array_texture.png",
|settings: &mut ImageLoaderSettings| {
settings.array_layout = Some(ImageArrayLayout::RowCount(4));
},
);
```
# Objective
Text Underline
## Solution
New `Underline` marker component, add to text entities to draw an
underline.
This PR is based on #21555, that should probably be reviewed and merged
first.
# Objective
This introduces a generalised 2d `Ring` shape for any underlying
primitive (i.e. what an `Annulus` is to a `Circle`). This allows us to
have "hollow" shapes or "outlines". `Ring` is also extrudable. It is
assumed that the inner and outer meshes have the same number of
vertices.
```rs
let capsule_ring = Ring::new(Capsule2d::new(50.0, 100.0), Capsule2d::new(45.0, 100.0));
let hexagon_ring = Ring::new(RegularPolygon::new(50.0, 6), RegularPolygon::new(45.0, 6)); // note vertex count must match
```
## Solution
There is a new generic primitive `Ring`, which takes as input any
`Primitive2d`, with two instances of that shape: the outer and the inner
(or hollow).
The mesh for a `RingMeshBuilder` is constructed by concatenating the
vertices of the outer and inner meshes, then walking the perimeter to
join corresponding vertices like so:
<img width="513" height="509" alt="image"
src="https://github.com/user-attachments/assets/2cecb458-3b59-44fb-858b-1beffecd1e57"
/>
```
# outer vertices, then inner vertices
positions = [
0 1 2 3 4
0' 1' 2' 3' 4'
]
# pairs of triangles
indices = [
0 1 0' 0' 1 1'
1 2 1' 1' 2 2'
2 3 2' 2' 3 3'
3 4 3' 3' 4 4'
4 0 4' 4' 0 0'
]
```
Examples of generated meshes:
<img width="398" height="351" alt="image"
src="https://github.com/user-attachments/assets/348bbd91-9f4e-4040-bfa5-d508a4308c10"
/>
<img width="472" height="376" alt="image"
src="https://github.com/user-attachments/assets/dbaf894e-6f7f-4b79-af3e-69516da85898"
/>
<img width="388" height="357" alt="image"
src="https://github.com/user-attachments/assets/cb9881e5-4518-4743-b8de-5816b632f36f"
/>
<img width="449" height="402" alt="image"
src="https://github.com/user-attachments/assets/7d2022c9-b8cf-4b4b-bb09-cbe4fe49fb89"
/>
## Testing
I've tested these changes by updating the `2d_shapes`, `3d_shapes` and
`custom_primitives` examples.
It could potentially benefit from unit tests.
---
## Showcase
<img width="1282" height="752" alt="image"
src="https://github.com/user-attachments/assets/edab9dbf-1093-43c7-9804-8e5c8a830573"
/>
_Rings of 2d primitives (bottom row)_
<img width="1282" height="752" alt="image"
src="https://github.com/user-attachments/assets/fbeed7f9-42bb-432c-bce9-cfeca87d70af"
/>
_Extrusions of rings of extrudable primitives (back row)_
---
## Follow-up work
I've only realised this from looking at Extrudable, but because I used
the mesh positions but it does assume the positions are well-ordered
around the perimeter. Extrudable instead uses the notion of a perimeter
(via indices so it doesn't matter what order the mesh positions are in),
a follow-up may be to do something similar for Ring. An alternative idea
may be to compute the perimeter first as directly a list of Vec2
positions (maybe a Perimeter trait), then construct any needed meshes
from that.
This potentially makes `Annulus` redundant as it is equivalent to a
`Ring<Circle>`. One thing of note is that `Extrusion<Annulus>` is
textured differently from `Extrusion<Ring<Circle>>`.
Another idea is to have a way to construct `PrimitiveTopology::LineList`
meshes from Primitive shapes (which may have a similar effect as
creating a Ring).
# Objective
Remove panics from image reinterpretation methods.
## Solution
Introduce new errors and emit those instead of panicking.
## Testing
- Did you test these changes? If so, how?
- CI + changed examples
- Are there any parts that need more testing?
- No
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- examples, no
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
- linux, amd cpu, amd gpu
---
Moved out of #20536
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
bevy v0.17.1, archlinux + i3wm.
`cargo run --example 2d_viewport_to_world`
```
thread 'Compute Task Pool (1)' (470531) panicked at /home/go/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/glam-0.30.8/src/u32/uvec2.rs:1071:23:
attempt to subtract with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `2d_viewport_to_world::controls`!
Encountered a panic in system `bevy_app::main_schedule::FixedMain::run_fixed_main`!
Encountered a panic in system `bevy_time::fixed::run_fixed_main_schedule`!
Encountered a panic in system `bevy_app::main_schedule::Main::run_main`!
```
## Solution
Consider window resize。
## Testing
no panic.
---
# Objective
Placing a player relative to a `TilemapChunk` but not in the tilemap
itself (for example, Pokemon's Trainer) requires calculating the
Transform of the relevant tile.
Animating these values (ex: Pokemon Trainer walking across tiles) also
requires Transforms
## Solution
Add a helper to calculate the transforms of tiles.
## Testing
```
cargo run --example tilemap_chunk
```
```rust
let mut transform = chunk.calculate_tile_transform(UVec2::new(5, 6));
transform.translation.z = 1.;
commands.spawn((
Mesh2d(meshes.add(Rectangle::new(8., 8.))),
MeshMaterial2d(materials.add(Color::from(RED_400))),
transform,
));
```
## Showcase
https://github.com/user-attachments/assets/97efeba0-fb09-4b77-9907-17710272d601
---
## Other
Maybe the function should return a z-index of 1 by default? I found
myself "raising" the z-index basically immediately... or Transform
should have a `with_z` function like the underlying Translation does.
# Objective
In the context of this PR, there are two access patterns users have when
reaching for examples:
- "I have a glTF file and I need to figure out what to do with it"
- "I want to learn about Bevy feature X"
Users wanting to learn about Bevy features can go to any appropriately
named examples directory to learn more about that feature.
Users who have a glTF file currently have to scan different directories
to find glTF related functionality.
For example: looking in the 2d directory for custom vertex attribute
examples.
## Solution
Create a glTF category and move applicable examples there.
It is notable that a "gltf" directory *could* accept any example that
uses a glTF file, but this should not be the case.
The examples in this directory should be focused on the "I have a glTF
file and I'm trying to figure out how to work with it" access pattern.
If a glTF file is used "as data" to support a feature showcase, then it
is not "a gltf example". For example: spawning a Scene from a glTF file
is not enough to qualify an example for this directory.
I chose to call this directory "gltf" instead of using the "scene"
directory so as to leave "scene" for bsn examples in the future.
## Testing
```
cargo run --example custom_gltf_vertex_attribute
cargo run --example load_gltf
cargo run --example load_gltf_extras
cargo run --example query_gltf_primitives
cargo run --example update_gltf_scene
cargo run --example edit_material_on_gltf
cargo run --example gltf_skinned_mesh
```
# Objective
When using the new tilemap chunk facilities, getting the data associated
with a specific tile is not currently exposed via API, making users do
the math themselves.
## Solution
Add a helper function to calculate the index position of `TileData`
given a chunk size and a position
## Testing
This example now includes a log that logs out when a specific tile's
index changes
```
cargo run --example tilemap_chunk
```
```
2025-09-21T07:36:23.489006Z INFO tilemap_chunk: tile_data changed tile_data=TileData { tileset_index: 2, color: LinearRgba(LinearRgba { red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 }), visible: true }
2025-09-21T07:36:32.514408Z INFO tilemap_chunk: tile_data changed tile_data=TileData { tileset_index: 3, color: LinearRgba(LinearRgba { red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 }), visible: true }
2025-09-21T07:36:37.040941Z INFO tilemap_chunk: tile_data changed tile_data=TileData { tileset_index: 2, color: LinearRgba(LinearRgba { red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 }), visible: true }
```
---
## Showcase
```rust
fn log_tile(tilemap: Single<(&TilemapChunk, &TilemapChunkTileData)>) {
let (chunk, data) = tilemap.into_inner();
let Some(tile_data) = data.tile_data_from_tile_pos(chunk.chunk_size, UVec2::new(3, 4)) else {
return;
};
// do something with tile_data
}
```
---
I alternatively considered storing the chunk size in the
`TilemapChunkTileData` as a private field, since the component hooks
guarantee the chunk_size needs to match between TilemapChunk and the
data. This would eliminate the chunk_size argument from the function but
also require re-setting the chunk size to reuse the same data with a
different chunk_size with the same length.
We could take this alternative approach, but I figured the simpler
helper would have a higher chance of making it into 0.17.
The function could also be named something like `data.at(chunk_size,
position)` instead of the longer name here.
prior art for storage helpers on arrays that are stored like this:
https://github.com/StarArawn/bevy_ecs_tilemap/blob/c2c1d076bd70d132edb6410e835a2ba6bdbaab5c/src/tiles/storage.rs#L43
# Objective
There are two enums both called `ScalingMode`,
`bevy_sprite::sprite::ScalingMode` and
`bevy_camera::projection::ScalingMode`, in violition of the one
namespace rule.
Part of #19285
## Solution
Rename `bevy_sprite::sprite::ScalingMode` to
`bevy_sprite::sprite::SpriteScalingMode`.
Chose to rename the sprite enum as
`bevy_camera::projection::ScalingMode` is much older and
`OrthographicProjectionScalingMode` is a bit long.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
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.
# Objective
works on #18238
## Solution
convert calls to `.with_children` to use the `Children::spawn` or
`Children::spawn_one` types or `children!` macro.
This touches the `window`, `2d`, `animation` folders, as well as
`ecs/one_shot_systems.rs`.
`observer_propagation.rs` looks like exactly what `with_children` is
useful for, so I deliberately haven't touched it.
I can break this up into more PRs or squash if desired.
## Testing
I've run the examples before and after this patch and verified visually
that nothing has changed
# Objective
Improve the readability of the float output in the bloom2d example by
formatting it to two decimal places.
This makes the output cleaner and more consistent.
## Solution
Used Rust’s formatting syntax (e.g., {:.2}) to ensure floats are
displayed with exactly two decimal places.
Applied this to all relevant print or debug output lines in the bloom2d
example.
---
# 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
# 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);
```
# Objective
Fixes#20615
## Solution
These shaders weren't updated when the import moved in #20587.
Fix the imports.
## Testing
```
cargo run --example custom_gltf_vertex_attribute
cargo run --example shader_material_2d
cargo run --example mesh2d_manual
```
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
Fixes#19531
## Solution
Move the `text2d` module from `bevy_text` into `bevy_sprite`
Move the `text2d` rendering module from `bevy_ui_render` to
`bevy_sprite_render`.
Remove the `bevy_sprite`, `bevy_window`, `bevy_camera` and
`bevy_transform` dependencies from `bevy_text`.
## Testing
```
cargo run --example text2d
```
## Showcase
# Objective
Add support for `TextBackgroundColor` to `Text2d`.
## Solution
Add a `TextBackgroundColor` query to `extract_text2d_sprite` and queue a
background sprite for each text section with a `TextBackgroundColor`.
## Testing
Added `TextBackgroundColor`s to some of the text in the `text2d`
example:
```cargo run --example text2d```
---
## Showcase
<img width="1924" height="1127" alt="text2d-background" src="https://github.com/user-attachments/assets/faa64dbe-cf80-4aca-b749-d24bb4399db3" />
# Objective
Add support for shadows to `Text2d`.
Fixes#19529
## Solution
* New `Text2dShadow` component.
`Text2dShadow` has identical fields to `bevy_ui`'s `TextShadow`. Only
the `Default` impl is different; since the y-axis is inverted for
`Text2d`, the default y-offset needs to be negative.
* `extract_text2d_sprite` now also draws shadow sprites when the
`Text2dShadow` component is present. Unlike in the `bevy_ui`
implementation, the shadows are extracted in the same system as the
glyph sprites to ensure correct ordering.
## Testing
Updated the `text2d` example to add some shadows to the text boxes:
```
cargo run --example text2d
```
## Showcase
<img width="1924" height="1127" alt="text-2d-shadows"
src="https://github.com/user-attachments/assets/ba3c0ae3-a962-4a62-9cee-ea7e328cd80c"
/>
# Objective
Many math primitives require const generics and thus are not
constructable at runtime and also not meshable. While the use of const
generics is theoretically more performant, it makes them very difficult
to interact with in a generic way, particularly in relationship to mesh
construction. For example, a ui that would allow selecting a primitive
in order to create a mesh.
## Solution
Make them alloc and meshable.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>