This guide is for engine integrators, technical leads, and architects who need to understand how Bevy is built, how to configure and deploy it, and how to operate and extend it at depth. It covers the workspace architecture, the ECS and rendering internals, the full configuration surface, security considerations, build/deployment targets, performance tuning, and known limitations.
Note
This guide documents the
0.19.0-devdevelopment line (themainbranch, crate version0.19.0-dev). Bevy releases roughly quarterly with breaking changes and official migration guides. Pin a specific version in production and treat each upgrade as a planned migration.
1. Executive Summary
Bevy is a free, open-source, data-driven game engine written in Rust. Its defining characteristics:
| Property | Detail |
|---|---|
| Language | Rust (edition 2024). MSRV tracks close to the latest stable release. |
| Architecture | A modular Cargo workspace of ~80 bevy_* crates, composed via a plugin system. |
| Core paradigm | Entity Component System (ECS) — data-oriented, automatically parallelized. |
| Rendering | Built on wgpu 29.x (Vulkan / Metal / DX12 / OpenGL / WebGPU / WebGL2). |
| Math | glam 0.32 (SIMD-accelerated), re-exported via bevy_math. |
| Windowing/Input | winit 0.30; gamepad via gilrs; accessibility via accesskit 0.24. |
| Licensing | Dual MIT OR Apache-2.0. |
| Targets | Windows, macOS, Linux, Web (Wasm), Android, iOS, and no_std. |
The engine's central design principle is modularity: every feature — including rendering itself — is a plugin gated behind a Cargo feature. Applications compose exactly the subset they need, trading functionality against compile time and binary size.
2. Architecture
2.1 The umbrella crate and sub-crates
The bevy crate is a thin facade. It contains almost no logic of its own; it re-exports the sub-crates as modules and pulls in bevy_internal:
bevy (umbrella facade, #![no_std])
└── pub use bevy_internal::*;
├── DefaultPlugins / MinimalPlugins (default_plugins.rs)
├── prelude (re-exports each sub-crate's prelude)
└── pub use bevy_app as app, bevy_ecs as ecs, bevy_render as render, …
Each module of bevy (except prelude) is published independently on crates.io as bevy_<name>. Consumers may depend on the umbrella crate for convenience or on individual crates for fine-grained control. bevy_internal exists as a separate crate specifically to enable simple dynamic linking (the bevy_dylib crate).
2.2 Crate map
The workspace is organized under crates/. The most significant crates, grouped by concern:
| Layer | Crates |
|---|---|
| Foundation | bevy_ecs (the ECS), bevy_app (App/Plugin/schedules), bevy_reflect (runtime reflection), bevy_ptr, bevy_platform (platform abstraction, no_std support), bevy_tasks (task pools), bevy_utils, bevy_derive, bevy_macro_utils. |
| Math & time | bevy_math (glam wrapper, curves, primitives, bounding volumes, sampling), bevy_transform, bevy_time. |
| Assets & scenes | bevy_asset (async loading, processing, IO backends), bevy_scene, bevy_world_serialization, bevy_gltf, bevy_image, bevy_mesh, bevy_shader, bevy_material. |
| Rendering | bevy_render (render graph, wgpu integration), bevy_core_pipeline, bevy_pbr (3D PBR), bevy_sprite / bevy_sprite_render (2D), bevy_camera, bevy_light, bevy_anti_alias, bevy_post_process, bevy_gizmos / bevy_gizmos_render, bevy_solari (experimental raytracing). |
| Windowing & input | bevy_window, bevy_winit, bevy_input, bevy_input_focus, bevy_gilrs, bevy_a11y, bevy_clipboard. |
| UI | bevy_ui, bevy_ui_render, bevy_ui_widgets, bevy_feathers, bevy_text. |
| App services | bevy_audio, bevy_animation, bevy_state, bevy_picking, bevy_log, bevy_diagnostic, bevy_dev_tools, bevy_remote, bevy_settings. |
Note
This separation is deliberate: a headless server can depend on
bevy_app+bevy_ecs+bevy_timewithout ever pulling inbevy_renderorwgpu. Thedefault_appanddefault_platformfeature collections exist to make this composition ergonomic.
2.3 The App and SubApp model
App is the top-level container. Internally it holds a SubApps collection: one "main" SubApp plus any number of labeled secondary sub-apps. Each SubApp owns its own World, schedules, and plugin registry. App's methods operate on the main sub-app unless you explicitly target another via sub_app_mut(label).
The renderer uses this: bevy_render runs a separate Render sub-app with its own world, and each frame "extracts" data from the main world into the render world. This is what allows pipelined rendering (the render world processing frame N while the main world simulates frame N+1).
App
├── SubApps.main → World (game state) + schedules
└── SubApps[Render] → World (GPU-facing state) + render schedules
(added by RenderPlugin; runs on its own thread when
PipelinedRenderingPlugin is active)
2.4 The plugin lifecycle
Plugins are added via App::add_plugins. Each plugin progresses through a four-phase lifecycle, tracked by PluginsState (Adding → Ready → Finished → Cleaned):
| Phase | Method | Purpose |
|---|---|---|
| build | Plugin::build(&self, app) |
Register systems, resources, types. Runs immediately on add_plugins. |
| ready | Plugin::ready(&self, app) -> bool |
Poll until asynchronous setup (e.g. GPU init) completes. |
| finish | Plugin::finish(&self, app) |
Final setup once all plugins are ready. |
| cleanup | Plugin::cleanup(&self, app) |
One-time teardown of build-time scaffolding. |
App::run drives this: it blocks while any plugin is in Adding, ticking the global task pools, then calls finish and cleanup before invoking the runner. Adding plugins after finish/cleanup panics. Duplicate plugins (by name) are rejected with AppError::DuplicatePlugin unless the plugin opts out of uniqueness via is_unique() == false.
2.5 DefaultPlugins composition (build order)
DefaultPlugins is defined with the plugin_group! macro in crates/bevy_internal/src/default_plugins.rs. Build order matters and is encoded explicitly. The sequence (each entry gated by its feature flag):
PanicHandlerPlugin → LogPlugin → TaskPoolPlugin → FrameCountPlugin → TimePlugin
→ TransformPlugin → DiagnosticsPlugin → InputPlugin → InputFocusPlugin/InputDispatchPlugin
→ [ScheduleRunnerPlugin if no window] / WindowPlugin + AccessibilityPlugin
→ TerminalCtrlCHandlerPlugin → WebAssetPlugin (if http/https) → AssetPlugin
→ WorldSerializationPlugin → ScenePlugin → WinitPlugin → [DlssInitPlugin]
→ RenderPlugin → ImagePlugin → MeshPlugin → CameraPlugin → LightPlugin
→ PipelinedRenderingPlugin → CorePipelinePlugin → PostProcessPlugin → AntiAliasPlugin
→ SpritePlugin → SpriteRenderPlugin → ClipboardPlugin → TextPlugin → UiPlugin
→ UiRenderPlugin → GltfPlugin → PbrPlugin → AudioPlugin → GilrsPlugin
→ AnimationPlugin → GizmoPlugin → GizmoRenderPlugin → StatesPlugin
→ [CiTestingPlugin] → [RenderDebugOverlayPlugin] → [HotPatchPlugin]
→ UiWidgetsPlugins → DefaultPickingPlugins → IgnoreAmbiguitiesPlugin
Ordering constraints worth noting (encoded in source comments):
WebAssetPluginloads beforeAssetPluginto register HTTP/HTTPS asset sources.WinitPluginloads afterAssetPluginbecause custom cursors are assets.ImagePluginloads afterRenderPluginso it knows the renderer's supported compressed texture formats.IgnoreAmbiguitiesPluginruns last to suppress known, benign system-ordering ambiguities between plugin pairs (e.g.bevy_uivsbevy_animationboth touchingTransforminPostUpdate).
MinimalPlugins is the bare runtime: TaskPoolPlugin → FrameCountPlugin → TimePlugin → ScheduleRunnerPlugin.
2.6 The schedule pipeline
The Main schedule (added by MainSchedulePlugin) runs a fixed ordered list of sub-schedules each frame. The full pipeline:
Startup (once): PreStartup → Startup → PostStartup
Per frame: First
PreUpdate
RunFixedMainLoop ──► (FixedFirst → FixedPreUpdate → FixedUpdate
→ FixedPostUpdate → FixedLast) × 0..N
Update
SpawnScene
PostUpdate
Last
RunFixedMainLoop consumes accumulated real time in fixed-size steps (Time<Fixed>, default 64 Hz), running the FixedMain schedule zero or more times to catch up. This decouples deterministic simulation from variable render frame rate.
2.7 ECS internals
bevy_ecs is the engine's foundation. Its storage and execution model:
| Concept | Implementation |
|---|---|
| Entity | A 32-bit index + generation. Lightweight handle; owns no data. |
| Component | Data with #[derive(Component)]. Stored either in Table (dense, default — cache-friendly for iteration) or SparseSet (fast add/remove) storage, selectable via #[component(storage = "...")]. |
| Archetype | The set of entities sharing an identical component set. The world has one archetype per unique component combination; archetypes are created but never freed. |
| Table | The columnar backing store. Multiple archetypes may share a table; they differ only by their sparse-set components. |
| Query | A typed view over matching archetypes. Access (&/&mut) is analyzed for conflicts so non-overlapping systems parallelize. |
| Edges | Cached archetype graph transitions for bundle insert/remove, avoiding recomputation when entities change shape. |
| Schedule executor | Runs a schedule's systems, parallelizing those with disjoint access. ApplyDeferred flushes queued Commands at sync points. |
Modules within bevy_ecs: archetype, bundle, change_detection, component, entity, entity_disabling, error, event, hierarchy, message, observer, query, reflect, relationship, resource, schedule, spawn, storage, system, world, lifecycle, name, template, traversal.
Important
Archetypes and tables are never garbage-collected — they persist until the
Worldis dropped. Code that rapidly creates and destroys entities with many distinct component combinations can grow archetype/table count unbounded. Prefer reusing entities or marker-component toggling over churning archetypes.
2.8 Communication models
Bevy offers three distinct mechanisms, each with different delivery semantics:
| Mechanism | Delivery | Use for |
|---|---|---|
Messages (Message, MessageReader/MessageWriter) |
Double-buffered queue, ~2-frame lifetime, polled | Decoupled, batch-processed, many-to-many communication. |
Observers (Event/EntityEvent, On<…>, add_observer) |
Immediate, synchronous, targeted | Reactive, must-handle, one-off responses. |
Lifecycle events (Add/Insert/Remove/Despawn, RemovedComponents<T>) |
Engine-fired on structural change | Reacting to component/entity lifecycle. |
The change-detection system (Added<T>, Changed<T>, Ref<T>, DetectChanges) is a fourth, pull-based reactive primitive built on per-component change ticks.
2.9 Rendering architecture
bevy_render integrates wgpu and defines a render graph — a DAG of render nodes (passes). Each frame the renderer:
- Extract — copies needed data from the main world into the render sub-app's world.
- Prepare — uploads GPU resources (buffers, textures, bind groups).
- Queue — assigns entities to render phases.
- Sort & Render — executes the render graph, encoding draw commands.
bevy_core_pipeline provides the 2D/3D core passes and tonemapping; bevy_pbr adds physically-based 3D shading, clustered lighting, shadows, prepass, deferred shading, SSAO/SSR, atmosphere, and (experimentally) meshlets and Solari raytracing. Shaders are written in WGSL and compiled via naga.
3. System Requirements
| Concern | Requirement |
|---|---|
| Rust | Recent stable (MSRV ≈ latest stable). Edition 2024 support required. |
| GPU | A backend supported by wgpu 29: Vulkan (Linux/Windows/Android), Metal (macOS/iOS), DirectX 12 (Windows), OpenGL/GLES (fallback), WebGPU/WebGL2 (web). |
| Linux display | X11 and/or Wayland (x11, wayland features; both default). Vulkan loader and drivers installed. |
| Web | A browser with WebGL2 or WebGPU; the wasm32-unknown-unknown target. |
| Android | NDK/SDK; API level 33 in examples; minimum API 26 if bevy_audio is used. |
| Disk/RAM (build) | A full-engine debug build is heavy; budget several GB of RAM and disk for target/. |
4. Configuration Mastery
Bevy is configured almost entirely in code, by customizing the plugins in DefaultPlugins via .set(...), plus a small number of environment variables. There is no central config file.
4.1 Window configuration (WindowPlugin)
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "My Game".into(),
resolution: (1920.0, 1080.0).into(),
present_mode: bevy::window::PresentMode::AutoVsync,
mode: bevy::window::WindowMode::Windowed,
resizable: true,
..default()
}),
// Quit the app when the last window closes:
exit_condition: bevy::window::ExitCondition::OnAllClosed,
close_when_requested: true,
..default()
})
| Field | Controls |
|---|---|
primary_window |
The initial window's properties (or None for headless). |
present_mode |
VSync behavior (AutoVsync, AutoNoVsync, Fifo, Immediate, Mailbox). |
mode |
Windowed / borderless / fullscreen. |
exit_condition |
When the app should exit relative to window closure. |
4.2 Renderer configuration (RenderPlugin / WgpuSettings)
The renderer is configured through RenderPlugin { render_creation: WgpuSettings { … } } (defined in crates/bevy_render/src/settings.rs):
use bevy::render::settings::{Backends, WgpuSettings, RenderCreation, PowerPreference};
use bevy::render::RenderPlugin;
DefaultPlugins.set(RenderPlugin {
render_creation: RenderCreation::Automatic(WgpuSettings {
backends: Some(Backends::VULKAN),
power_preference: PowerPreference::HighPerformance,
..default()
}),
..default()
})
| Setting | Effect |
|---|---|
backends |
Restrict to specific wgpu backends (VULKAN, METAL, DX12, GL, BROWSER_WEBGPU). |
power_preference |
HighPerformance (discrete GPU) vs LowPower (integrated). |
features / limits |
Required wgpu Features and Limits. |
priority |
Read from the WGPU_SETTINGS_PRIO environment variable to bias compatibility vs. functionality. |
Tip
wgpu also honors its own environment variables independent of Bevy:
WGPU_BACKENDforces a backend (vulkan,metal,dx12,gl), andWGPU_POWER_PREFsets the power preference. These are useful for debugging GPU-selection issues without recompiling.
4.3 Logging configuration (LogPlugin)
bevy_log integrates with the tracing ecosystem.
use bevy::log::LogPlugin;
DefaultPlugins.set(LogPlugin {
level: bevy::log::Level::INFO,
filter: "wgpu=error,bevy_render=info,my_game=debug".into(),
..default()
})
| Field | Controls |
|---|---|
level |
The minimum log level. |
filter |
A per-target RUST_LOG-style filter string. |
custom_layer |
Hook to add your own tracing layers. |
Environment variable: TRACE_CHROME (read in crates/bevy_log/src/lib.rs) — when the trace_chrome feature is enabled, controls the output path of the Chrome-format trace file. The RUST_LOG environment variable also overrides the filter at runtime.
4.4 Asset configuration (AssetPlugin)
use bevy::asset::AssetPlugin;
DefaultPlugins.set(AssetPlugin {
file_path: "assets".into(),
watch_for_changes_override: Some(true), // hot-reload (needs file_watcher feature)
..default()
})
bevy_asset supports multiple IO backends (file, embedded, web/HTTP) and an optional asset processor (the asset_processor feature) that transforms source assets into optimized runtime assets. Web asset sources are enabled by the http / https features and the WebAssetPlugin.
4.5 Task pools (TaskPoolPlugin)
TaskPoolPlugin configures the compute/async-compute/IO thread pools. By default it sizes them based on available CPU cores. Override via TaskPoolOptions if you need to reserve cores. The multi_threaded feature (default on) enables parallel execution; disabling it forces all engine work onto one thread (useful for Wasm and debugging).
4.6 Environment variable summary
| Variable | Read by | Effect |
|---|---|---|
RUST_LOG |
bevy_log (tracing) |
Override log filter at runtime. |
TRACE_CHROME |
bevy_log (trace_chrome feature) |
Chrome trace output path. |
WGPU_SETTINGS_PRIO |
bevy_render settings |
Bias wgpu settings priority (compatibility/functionality). |
WGPU_BACKEND |
wgpu | Force a specific GPU backend. |
WGPU_POWER_PREF |
wgpu | Force power preference. |
CARGO_MANIFEST_DIR |
build tooling (build-easefunction-graphs) |
Build-time asset path resolution. |
GITHUB_ACTIONS |
CI example harness | Detect CI for automated example testing. |
5. Security Analysis
Bevy is a client-side game engine, but several subsystems have real security implications, especially for networked or web-deployed games.
5.1 Remote asset loading (HTTP/HTTPS)
The http and https features enable WebAssetPlugin, letting the engine download assets from URLs at runtime.
Caution
Loading assets over the network executes parsing/decoding code (image, glTF, audio decoders) on untrusted, attacker-controllable data. The feature documentation explicitly warns of security implications. Only enable
http/httpswhen you control the asset sources, validate URLs, and preferhttpsoverhttp. Theweb_asset_cachefeature caches downloads on disk but never invalidates entries — a poisoned cache entry persists.
5.2 The Bevy Remote Protocol (bevy_remote)
bevy_remote (the bevy_remote feature) exposes the Bevy Remote Protocol — a remote interface for inspecting and mutating a running app's world (used by editors and tooling).
Warning
The remote protocol grants powerful read/write access to the ECS world. Treat any open remote port as an administrative interface: bind it to localhost, never expose it on untrusted networks, and disable the feature entirely in shipped builds unless required.
5.3 Deserialization (scenes & reflection)
bevy_scene and bevy_world_serialization deserialize world state via bevy_reflect and serde. Loading a scene file instantiates components by reflected type.
Important
Treat scene/world files as code-equivalent trust boundaries. Only load scene files from trusted sources; a malicious scene can spawn arbitrary registered components and resources. Validate or sandbox any user-supplied scene content.
5.4 Native code and unsafe
bevy_ecs uses unsafe deliberately for performance (storage access, parallel execution). bevy_internal and the umbrella crate set #![forbid(unsafe_code)], isolating unsafety to the lower-level crates where it is audited (the workspace lints enforce undocumented_unsafe_blocks = "warn" and unsafe_op_in_unsafe_fn = "warn"). Optional C-backed dependencies (zstd_c, statically-linked-dxc) introduce native code; the pure-Rust alternatives (zstd_rust) are the safe defaults.
5.5 Dependency surface
The engine pulls in a large dependency tree (wgpu, winit, glam, image codecs, audio codecs, accesskit). Audit transitive dependencies with cargo audit / cargo deny in CI, and minimize the feature set to shrink the attack surface. Disabling image/audio format features you don't use removes their decoders entirely.
6. Build, Packaging & Deployment
6.1 Profiles and optimization
# Optimize dependencies even in dev builds (big runtime win, small build cost):
[profile.dev.package."*"]
opt-level = 3
[profile.dev]
opt-level = 1
# A tuned release profile:
[profile.release]
lto = "thin"
codegen-units = 1
| Build | Command |
|---|---|
| Dev | cargo run |
| Release | cargo run --release / cargo build --release |
| Fast iteration | cargo run --features bevy/dynamic_linking |
Caution
dynamic_linkingmust be disabled for shipped builds — it requires the Bevy shared library at runtime and is unsupported on Wasm.
6.2 Desktop packaging
Ship the executable and the assets/ directory (the AssetServer resolves paths relative to it). On Linux, ensure end users have Vulkan drivers. Consider cargo bundling tools for .app/.msi/AppImage packaging.
6.3 Web (WebAssembly)
rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown
# then wasm-bindgen / trunk / wasm-server-runner to produce JS glue and serve
Pick exactly one web backend:
| Feature | Trade-off |
|---|---|
webgl2 |
Broadest browser support; no compute shaders; some advanced effects unavailable. |
webgpu |
Best performance and full feature set; narrower browser support. Overrides webgl2 when both are set. |
The web feature enables browser API access; the bevy_dylib-style dynamic linking is unavailable on Wasm. Consider multi_threaded carefully — threading support on the web requires cross-origin isolation headers.
6.4 Mobile
| Platform | Notes |
|---|---|
| Android | android-game-activity (default, GameActivity) or android-native-activity (legacy, for cargo-apk). Targets: aarch64-linux-android, armv7-linux-androideabi. Examples target API 33; bevy_audio requires API ≥ 26. bevy_android provides the entry glue. |
| iOS | An Xcode project (examples/mobile) drives a static/cdylib build. App::run never returns on iOS (event-loop-owned). |
6.5 Headless / server deployment
For dedicated servers and CLI tools, avoid the renderer entirely:
bevy = { version = "0.18", default-features = false, features = ["default_app"] }
use bevy::prelude::*;
use bevy::app::ScheduleRunnerPlugin;
use std::time::Duration;
App::new()
.add_plugins(MinimalPlugins.set(
ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(1.0 / 60.0))
))
.run();
Note
Without a fixed
run_loopduration,ScheduleRunnerPluginspins as fast as possible and saturates a CPU core. Always cap the tick rate for servers.
7. Operations
7.1 Diagnostics
bevy_diagnostic (always included) collects runtime metrics. Add the built-in plugins to surface them:
use bevy::diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin};
app.add_plugins((FrameTimeDiagnosticsPlugin::default(), LogDiagnosticsPlugin::default()));
This logs FPS, frame time, and entity counts. The sysinfo_plugin feature adds CPU/memory system diagnostics. EntityCountDiagnosticsPlugin reports live entity counts — watch this to catch archetype/entity leaks.
7.2 Tracing and profiling
| Feature | Tool |
|---|---|
trace |
Base tracing spans throughout the engine. |
trace_tracy |
Stream spans to the Tracy profiler (frame-by-frame timeline). |
trace_tracy_memory |
Tracy with allocation tracking. |
trace_chrome |
Emit a Chrome-tracing JSON file (path via TRACE_CHROME). |
detailed_trace |
Compile-in extra-fine spans (off by default; expensive). |
7.3 Developer tools
The dev feature collection (debug + bevy_dev_tools + file_watcher) enables hot-reloading and debug overlays. bevy_dev_tools includes CI testing harnesses, a frame-time graph, schedule inspection (schedule_data feature), and (with bevy_pbr) a render debug overlay. The bevy_debug_stepping feature enables stepping execution through systems one at a time.
Warning
The
devcollection andbevy_dev_toolsare for development only. Never enable them in shipped builds — they add overhead and expose internals.
7.4 Logs
Logging is driven by tracing. Control verbosity with the LogPlugin filter or the RUST_LOG environment variable. A common production filter quiets noisy subsystems: RUST_LOG="wgpu=error,naga=error,info".
8. Performance Tuning
| Lever | Guidance |
|---|---|
| Feature trimming | Disable unused Cargo features (image/audio formats, experimental renderers). Fewer features = faster builds, smaller binaries, less surface. |
multi_threaded |
Keep enabled for parallel system execution on native targets. |
| Change detection | Use Changed<T>/Added<T> to avoid recomputing unchanged data every frame. |
| Query design | Prefer Table storage (default) for components iterated in hot loops; SparseSet for components frequently added/removed. |
| Fixed timestep | Put heavy simulation in FixedUpdate so it runs at a bounded rate, not per render frame. |
| System ordering | Avoid over-constraining order; unordered systems parallelize. Only add .before/.after/.chain where correctness requires. |
| Batching / instancing | The PBR/sprite renderers automatically batch and GPU-instance compatible draws. Stress examples (bistro, caldera_hotel) expose flags to toggle frustum/occlusion culling, automatic batching, and indirect drawing for profiling. |
| Pipelined rendering | PipelinedRenderingPlugin (auto-enabled on native + multi_threaded) overlaps the render world with the next simulation frame. |
| Texture compression | Use compressed formats (KTX2 with zstd_rust/zstd_c, Basis Universal, BCn) to cut VRAM and load time. |
| Benchmarks | The benches crate uses Criterion; run with `cargo bench -p benches --bench <ecs |
Tip
The renderer auto-batches and auto-instances by default. If profiling a draw-call bottleneck, the large-scene examples document
--no-automatic-batching,--no-frustum-culling,--no-indirect-drawing, and--no-cpu-cullingflags to isolate which stage is the cost.
9. Repository Structure (for contributors)
bevy/
├── crates/ ~80 bevy_* sub-crates (the engine)
├── src/lib.rs Umbrella facade: pub use bevy_internal::*
├── examples/ Hundreds of runnable examples, grouped by topic
│ ├── 2d/ 3d/ ui/ ecs/ shader/ animation/ audio/ …
│ ├── mobile/ Android (GameActivity & native) + iOS projects
│ ├── stress_tests/ Performance stress scenarios
│ └── large_scenes/ bistro, caldera_hotel, bevy_city (real-world scenes)
├── benches/ Criterion benchmarks (ecs, render, reflect, …)
├── tools/ Internal tooling
│ ├── ci/ The CI command runner (cargo run -p ci)
│ ├── build-templated-pages/ Generates docs/cargo_features.md
│ ├── build-wasm-example/ Wasm example builder
│ └── example-showcase/ Example catalog/screenshot generator
├── tests/ tests-integration/ Integration tests
├── errors/ Error code documentation
├── docs/ cargo_features.md and other docs
└── assets/ Example assets (not shipped in published crates)
9.1 Contributing workflow
- CI is orchestrated by
tools/ci(cargo run -p ci), which runs formatting, clippy, tests, doc checks, and compile-fail tests on stable Rust. - Compile-fail tests (
crates/*/compile_fail) assert exact compiler errors for macros; they live outside the workspace to avoid breakingcrater. - Workspace lints are strict:
undocumented_unsafe_blocks,unsafe_op_in_unsafe_fn,allow_attributes_without_reason,std_instead_of_core(forno_stdcrates), and many clippy pedantic lints are set towarn. no_stdsupport: foundation crates compile withoutstd. CI checks againstx86_64-unknown-none,wasm32v1-none, andthumbv6m-none-eabitargets usinglibm+critical-section.- RFCs for large architectural changes go to the bevyengine/rfcs repository.
10. Quick Reference
10.1 App builder methods
| Method | Purpose |
|---|---|
App::new() / App::empty() |
Full / minimal app scaffolding. |
add_plugins(p) |
Install plugin(s) or group(s). |
add_systems(schedule, sys) |
Add systems to a schedule. |
configure_sets(schedule, sets) |
Order/gate system sets. |
insert_resource(v) / init_resource::<T>() |
Provide resources. |
add_message::<M>() |
Enable a message type. |
add_observer(obs) |
Register a global observer. |
register_type::<T>() |
Register a reflected type. |
register_required_components::<T, R>() |
Declare R required by T. |
set_runner(f) |
Replace the main-loop runner. |
run() |
Build and start the app. |
10.2 Schedules
| Group | Schedules |
|---|---|
| Startup | PreStartup, Startup, PostStartup |
| Main (per frame) | First, PreUpdate, RunFixedMainLoop, Update, SpawnScene, PostUpdate, Last |
Fixed (in RunFixedMainLoop) |
FixedFirst, FixedPreUpdate, FixedUpdate, FixedPostUpdate, FixedLast |
10.3 Feature profiles & collections
| Tier | Examples |
|---|---|
| Profiles | default, 2d, 3d, ui |
| Collections | dev, audio, audio-all-formats, scene, picking, default_app, default_platform, common_api, default_no_std |
| Render backends | webgl2, webgpu, raw_vulkan_init |
| Platform | x11, wayland, android-game-activity, std, libm, critical-section |
10.4 Key environment variables
RUST_LOG, TRACE_CHROME, WGPU_SETTINGS_PRIO, WGPU_BACKEND, WGPU_POWER_PREF.
11. Known Limitations & Improvement Areas
Drawn from the engine's own warnings, feature documentation, and the community issue tracker at the documented commit.
11.1 Engine-level caveats
- Pre-1.0, breaking releases — the API changes roughly quarterly; the README explicitly warns important features are still missing and documentation is sparse.
- Archetypes/tables never freed — high-churn entity shapes can grow memory unbounded (§2.7).
- MSRV churn — tracks latest stable Rust closely; older toolchains are unsupported.
- WebGL2 limits — no compute shaders; several rendering features are WebGPU-only.
web_asset_cachenever invalidates — cached downloads persist indefinitely (§5.1).- Experimental subsystems —
bevy_solari(raytracing),meshlet,dlss,experimental_pbr_pcss, and ghost UI nodes are explicitly experimental and may change or carry GPU-compatibility risk.
11.2 Community-reported issues (at commit 457b918)
| Issue | Status | Summary |
|---|---|---|
| #24525 | Open | bevy_dev_tools didn't receive a release candidate. |
| #24521 | Open | Docs for bevy::asset::io::android are not generated. |
| #19625 | Open | objc2 framework panics on a macOS 26 developer beta. |
| #24508 | Open | MouseScrollUnit handling is inconsistent — input-scaling caveat for scroll. |
| #24517 | Open | AtmosphereEnvironmentMapLight / GeneratedEnvironmentMapLight are very slow — a performance watch item. |
| #24510 | Open | Re-enable the std_instead_of_core lint (no_std hygiene). |
| #24483 | Open | bevy_extract tracking (planned extract-stage refactor). |
| #24356 | Closed | FeathersNumberInput failed when spawned in Update — fixed. |
| #24346 | Closed | "CommandQueue has un-applied commands" on delayed-command exit — fixed. |
Note
Issue counts are a point-in-time snapshot. The live tracker is the authoritative source: https://github.com/bevyengine/bevy/issues.
11.3 Recommendations for integrators
- Pin and plan migrations. Lock to a published version and schedule upgrades around the quarterly release + migration guide cadence.
- Trim features aggressively. Start from
default-features = falseand a profile (2d/3d/default_app), adding only what you need. - Gate dev/remote features out of release builds. Ensure
bevy_dev_tools,dev, andbevy_remoteare absent from shipped binaries. - Treat asset/scene/network inputs as untrusted. Validate sources; prefer pure-Rust decoders; keep
httpoff unless required. - Add diagnostics early. Wire up
FrameTimeDiagnosticsPluginand entity-count diagnostics from day one to catch leaks and regressions.