Open-Source Wikis

/

Bevy

/

Packages

/

bevy_app

bevyengine/bevy

bevy_app

The application-layer crate. Owns the App, the plugin system, the schedule labels every other crate composes against, and the runners that drive the main loop. Almost every other crate in the workspace depends on bevy_app.

Purpose

bevy_app defines the top-level container that composes everything else. It does three things:

  1. Provides App, SubApp, and the Plugin / PluginGroup traits used to compose features.
  2. Defines the canonical schedule labels (Update, PostUpdate, FixedUpdate, etc.) that the rest of the engine targets.
  3. Provides runners — the loop drivers that call World::run_schedule(Main) repeatedly, including the headless ScheduleRunnerPlugin and the panic / Ctrl-C handlers.

This crate is intentionally small and contains no game logic.

Directory layout

crates/bevy_app/src/
├── app.rs                       # App + AppExit
├── sub_app.rs                   # SubApp container
├── plugin.rs                    # Plugin trait
├── plugin_group.rs              # PluginGroup trait + plugin_group! macro
├── main_schedule.rs             # Update, PostUpdate, FixedUpdate, …
├── schedule_runner.rs           # Headless runner plugin
├── panic_handler.rs             # tracing-friendly panic hook
├── terminal_ctrl_c_handler.rs   # Catch Ctrl-C → AppExit
├── hierarchy.rs                 # App helpers for spawning hierarchies
├── propagate.rs                 # Hierarchy propagation utilities
├── task_pool_plugin.rs          # Initializes ComputeTaskPool/AsyncComputeTaskPool/IoTaskPool
├── hotpatch.rs                  # Optional hot-patching support (behind feature flag)
└── lib.rs

Key abstractions

Type File One-line description
App crates/bevy_app/src/app.rs The root container. Holds sub-apps, plugins, and the runner.
SubApp crates/bevy_app/src/sub_app.rs A self-contained world + schedules. Used for the renderer.
Plugin crates/bevy_app/src/plugin.rs Trait. build(&self, &mut App) is the unit of feature composition.
PluginGroup crates/bevy_app/src/plugin_group.rs Composes related plugins; supports per-element disable/replace.
AppExit crates/bevy_app/src/app.rs Event the runner watches for to stop the loop.
Update etc. crates/bevy_app/src/main_schedule.rs The standard schedule labels.
ScheduleRunnerPlugin crates/bevy_app/src/schedule_runner.rs Headless runner — runs the Main schedule in a loop.
PanicHandlerPlugin crates/bevy_app/src/panic_handler.rs Sets a panic hook that emits a tracing::error!.
TaskPoolPlugin crates/bevy_app/src/task_pool_plugin.rs Initializes the global ComputeTaskPool/AsyncComputeTaskPool/IoTaskPool.

How it works

App is mostly a thin wrapper around a default SubApp plus a registry of additional named sub-apps. The render SubApp (added by bevy_render::RenderPlugin) is the canonical example.

graph TD
    App --> Main[Main SubApp]
    App --> Render[RenderApp SubApp]
    App --> Other["… any number of named SubApps"]
    Main --> MS[Schedules: Update/PostUpdate/FixedUpdate/…]
    Main --> MW[World]
    Render --> RS[Schedules: ExtractSchedule/Render]
    Render --> RW[World]

The runner is a Box<dyn Fn(App) -> AppExit> stored on App. App::run() invokes the runner. Plugins can replace the runner — WinitPlugin does this so the OS event loop becomes the main driver, calling world.run_schedule(Main) once per frame.

Schedule labels are defined as zero-sized types deriving ScheduleLabel. The full list (in execution order on the main schedule):

  1. First
  2. PreUpdate
  3. StateTransition
  4. RunFixedMainLoop (which itself runs FixedFirst → FixedPreUpdate → FixedUpdate → FixedPostUpdate → FixedLast zero or more times)
  5. Update
  6. SpawnScene
  7. PostUpdate
  8. Last

Plus PreStartup, Startup, PostStartup (each runs exactly once on the first tick).

Plugin composition

Adding a plugin:

App::new()
    .add_plugins(DefaultPlugins)
    .add_plugins(MyPlugin)
    .run();

A plugin group is a more powerful unit:

App::new()
    .add_plugins(DefaultPlugins.build()
        .disable::<AudioPlugin>()
        .add_after::<TimePlugin, _>(MyTimePlugin))
    .run();

The plugin_group! macro in plugin_group.rs generates a PluginGroup impl from a struct + enabled list, including per-feature cfg gates. DefaultPlugins and MinimalPlugins are generated this way (see crates/bevy_internal/src/default_plugins.rs).

Integration points

  • Depends on: bevy_ecs, bevy_reflect, bevy_tasks, bevy_platform, bevy_utils.
  • Depended on by: Every other engine crate. Adding a feature means writing a Plugin from this crate.
  • Runner override hook: plugins can call app.set_runner(...) exactly once.

Entry points for modification

  • Adding a new schedule label: define a struct deriving ScheduleLabel in your plugin and call app.init_schedule(MyLabel). Look at main_schedule.rs for the canonical pattern.
  • Adding a new SubApp: app.insert_sub_app(MyAppLabel, SubApp::new()). The render plugin shows how.
  • Replacing the runner: call app.set_runner(my_runner) from a plugin. Don't do this lightly — most code paths assume the standard runner.
  • Adding terminal/Ctrl-C handling: edit terminal_ctrl_c_handler.rs (Unix/Windows only).

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

bevy_app – Bevy wiki | Factory