Bevy 0.20
Thanks to 227 contributors, 817 pull requests, community reviewers, and our generous donors, we’re happy to announce the Bevy 0.20 release on crates.io!
For those who don’t know, Bevy is a refreshingly simple data-driven game engine built in Rust. You can check out our Quick Start Guide to try it today. It’s free and open source forever! You can grab the full source code on GitHub. Check out Bevy Assets for a collection of community-developed plugins, games, and learning resources.
To update an existing Bevy App or Plugin to Bevy 0.20, check out our 0.19 to 0.20 Migration Guide.
Since our last release a few months ago we’ve added a ton of new features, bug fixes, and quality of life tweaks, but here are some of the highlights:
Solari, Bevy’s realtime pathtraced renderer, has seen major improvements to pretty much every aspect of the plugin!
Read JMS55’s blog for the technical details, or continue reading below for the high level overview.
Thanks to improvements in our ReSTIR implementation, rendering is now mostly unbiased, leading to much more accurate lighting.
Additionally, thanks to some other changes, moving objects no longer have shadows that lag behind, and reflections now look significantly less shimmery in motion, especially for non-metallic materials.
DLSS-RR has gotten very good in recent updates, and for many scenes, ReSTIR costs a decent chunk of performance, and does not significantly improve image quality.
As a result, we’ve decided to make ReSTIR optional, and turn it off by default.
If you were using Solari in Bevy 0.19, check if the loss of ReSTIR affects your scene, and if so re-enable SolariLighting::restir.
With ReSTIR off, expect reduced shadow quality and missing shadows in motion in scenes with many lights. We are exploring cheaper ways of improving light sampling, without ReSTIR, to improve this in the future.
In addition, Solari’s scene management code is now retained (similar to retained render world optimizations in previous versions of Bevy), and overall much more optimized, leading to significantly reduced CPU costs.
You may also want to take a look at the new fields in SolariLighting. While we aim to set reasonable defaults that will work well across a wide variety of games, there are now many knobs (world cache size, per-pixel light sample count, temporal accumulation, and path tracing bounce count) that can be tweaked to improve performance or quality for your specific scene, project and hardware.
Solari now supports lighting from Atmosphere and EnvironmentMapLights on cameras, in addition to the existing support for DirectionalLight and emissive meshes. We’re hoping to add support for the remaining PointLight, SpotLight, and RectLight types in the near future.
Solari now also runs on macOS, but note that there is currently no built-in denoiser included in bevy_solari for macOS. MetalFX Ray Reconstruction might be a possible solution in the future (contributions welcome!)
Finally, our dlss_wgpu crate has been updated to support the latest version of DLSS, bringing support for DLSS-RR 4.5, which significantly improves denoising quality in Solari.
If you were using DLSS in Bevy 0.19, make sure to download and setup the newest version of the DLSS SDK, else you will run into compiler errors.
BSN landed with a few idiosyncrasies that caused friction in practice. We made some changes to BSN’s syntax this cycle in the interest of improving its ergonomics and clarity. After this, the syntax should largely be nailed down.
All scene references now require @ prefixes:
In addition to making it easier to spot scene inclusions (and unifying the syntax across cases), this freed us up to make component values much easier to work with!
You can now remove all of those pesky template_value wrappers from your component values:
Enums no longer require VariantDefaults or FromTemplate, provided they implement Default and Clone:
If you were using an enum that didn’t support VariantDefaults, you can now remove the template_value wrapper:
The removal of VariantDefaults does mean that enums must now have every field specified:
We believe this tradeoff is worth it, as it increases BSN’s compatibility with arbitrary Rust enums. Rust doesn’t support “enum variant defaults” anyway!
The “builder pattern” (and chained methods generally) previously required a template_value wrapper. This can now be removed:
Additionally, you can now remove the template_value wrapper in cases like this:
In general, you should now be able to remove all template_value instances from your BSN declarations!
BSN previously used commas to separate entities, with optional () around entities to make the boundaries clearer. This resulted in a lot of syntax noise, line noise, and over-indentation:
To avoid this, many developers opted for this syntax instead, which made it very hard to visually distinguish entities:
BSN now uses — to separate entities in a list:
This gives us the best of all worlds: entities are visually distinct, and there is no over-indentation, line noise, or syntax noise (the stats when compared to other competitors in the “markup format” space are very competitive!). Both () and , have been deprecated in this context.
Using [] and () for bsn_list! (and bsn!) is now discouraged / warned against (ex: bsn_list! []), as it can result in poor rustfmt autoformatting. Instead, use bsn_list! {}, which is the only syntax that rustfmt won’t touch. Don’t worry, we plan to build a BSN auto-formatter!
We landed BSN, Bevy’s next generation scene system, in our last release. It was missing a key piece though: the ability to easily run logic when a scene is fully “ready” and spawned (ex: all dependencies have loaded, the full hierarchy is present, and all of the initial components are inserted in the scene). This is a critical piece for building cohesive, standalone, composable scenes. It is also necessary to properly layer Bevy logic on top of other scene representations (like glTF).
The closest we had was the Add event for a given component, which runs “top down” (meaning children are not available). We needed a “bottom up” equivalent to enable building logic that relies on the complete loaded and spawned scene.
The solution is pretty straightforward: trigger a new Ready event for each entity in a spawned scene after the full spawn logic has run for that entity (including its descendants).
This enables the following:
Feathers, Bevy’s opinionated editor-centric UI toolkit, now has more widgets for you to play with:
Bevy now has a compact color input selector that displays a color picker widget popup when clicked. This includes a color wheel selector, RGB, and HSL selectors, and a recently used colors grid.
A scrollable, selectable list view.
A selection field that when clicked, displays a dropdown containing a list of options to select.
Spawns a menu popup when the menu is opened and despawns it when it is closed. This is in contrast to the normal Menu widget, which just hides the menu.
The FeathersNumberInput widget has been expanded to support both normal text input and scrubbing / dragging. There is a configurable “hard limit” (minimum and maximum value via any input method) and “soft limit” (minimum and maximum value via dragging), in addition to control over floating point precision and step sizes.
bevy_ui_widgets now has headless (bring-your-own-visuals) tab behavior: a TabList container and Tab headers.
Selection is managed “externally”. SelectedTab on the list holds the selected tab; interaction emits ValueChange<Option
Tabs support keyboard shortcuts and integrate with Bevy’s focus, interaction, and accessibility systems.
See the headless_tabs example for controlled and self-updating tab lists in both orientations.
Bevy’s shaders are now written in WESL and the old “Custom Bevy Extended WGSL” language support has been removed.
WESL is a language standard that extends WGSL to add important usability features like modules, imports, conditional compilation, and more. You can see what that looks like (and render pretty shader toys!) live in your browser in the WESL Playground.
Bevy has historically handled these things in our own custom WGSL dialect, but we believe it is better for the wider shader ecosystem (and for us) to adopt a common standard where we can pool resources on language improvements, module ecosystems, and IDE tooling. We’ve been working closely with the WESL team to evolve the standard in a way that fits well into the Bevy picture.
A critical part of that tooling is language server protocol support, in the form of wgsl-analyzer. That means syntax highlighting, go-to-definition, inlay hints, code folding, formatting and more, once installed for your IDE of choice.
Custom shaders in the old Bevy WGSL dialect need to be translated to WESL and renamed from .wgsl to .wesl. Plain WGSL files with no preprocessor directives will keep working.
Mesh shaders are now integrated with Bevy’s pipeline cache and are available for advanced users to take advantage of. Mesh shaders can be used to render:
Mesh shaders, at a high level, replace the classic vertex shader with a compute shader. This allows generating geometry directly on the GPU and passing those generated primitives directly to the fragment shader without using multiple pipelines or intermediary buffers (to pass data from a compute shader to a render pipeline).
A MeshPipeline contains:
The new MeshPipelineDescriptor can be used to define a MeshPipeline. That MeshPipeline is then used as a RenderPipeline, which allows the re-use of Bevy’s lower level rendering APIs such as RenderContext::begin_tracked_render_pass to take advantage of the new draw_mesh_tasks APIs.
It is notable that mesh shaders are an advanced graphics approach with platform-specific performance considerations, and that this is the initial base support for the feature. Higher level user APIs, and easy integration with Bevy’s StandardMaterial, are left to future work.
Mesh shaders are not supported on web platforms.
Check out the new mesh_shader_intro example for more usage examples.
Until now, Bevy’s sprite renderer has been lacking a major feature: the ability to extend it with custom shaders! With this release, it’s now possible to create custom materials for sprites by implementing the MaterialExtension2d trait, inserting the SpriteMaterial component and adding the SpriteMaterialPlugin to your app.
The shader can use functions exported from bevy_sprite_render::sprite_mesh::functions, including:
Check out the sprite_material example to see it in action!
Bevy now provides a 2D analog to 3D’s ExtendedMaterial, which can be used to extend an existing material by implementing the MaterialExtension2d trait:
This material can now be used in an ExtendedMaterial2d struct:
The sprite render backend was replaced by a new backend that reuses a lot of the infrastructure made for 3D. This resulted in improved performance in many cases and also makes future maintenance and improvements easier.
We have upstreamed the awesome bevy_editor_cam made by @aevyrie as the new PanOrbitCamera in our bevy_camera_controller crate!
Add MeshPickingPlugin and DefaultPanOrbitCameraPlugins:
Then add the PanOrbitCamera component on any 3D camera.
Full functionality is shown in the camera/pan_orbit_camera_cad example.
Ordering large groups of systems with .chain() is convenient, but it can be overly strict. If system set X is chained before system set Y, every system in X must finish before any system in Y can start, even when the systems involved never touch the same data. This often leaves worker threads idle while they wait for a handful of stragglers at the end of a system set, a pattern that shows up frequently in the render world.
The new chain_weak(), before_weak(), and after_weak() functions provide a looser alternative. Like their regular counterparts, they request an ordering between successive elements, however that ordering is only kept between systems whose data accesses actually conflict. Systems that don’t conflict are left unordered and may run in any order, including in parallel.
When two weakly-ordered systems actually conflict on their data access, a normal ordering is kept between them, so the earlier one still runs first. Two systems that conflict only through a non-conflicting system between them in the chain stay ordered as well. Non-conflicting systems, however, are left free to run in any order and overlap for increased parallelism!
Two kinds of system are treated as always conflicting, so their ordering is always kept: an earlier system that produces deferred effects such as Commands (so the later system observes them, with an ApplyDeferred sync point inserted as usual), and exclusive systems (which cannot overlap anything regardless).
Because the scheduler can only see accesses it