Refactor App and SubApp internals for better separation (#9202)

# Objective

This is a necessary precursor to #9122 (this was split from that PR to
reduce the amount of code to review all at once).

Moving `!Send` resource ownership to `App` will make it unambiguously
`!Send`. `SubApp` must be `Send`, so it can't wrap `App`.

## Solution

Refactor `App` and `SubApp` to not have a recursive relationship. Since
`SubApp` no longer wraps `App`, once `!Send` resources are moved out of
`World` and into `App`, `SubApp` will become unambiguously `Send`.

There could be less code duplication between `App` and `SubApp`, but
that would break `App` method chaining.

## Changelog

- `SubApp` no longer wraps `App`.
- `App` fields are no longer publicly accessible.
- `App` can no longer be converted into a `SubApp`.
- Various methods now return references to a `SubApp` instead of an
`App`.
## Migration Guide

- To construct a sub-app, use `SubApp::new()`. `App` can no longer
convert into `SubApp`.
- If you implemented a trait for `App`, you may want to implement it for
`SubApp` as well.
- If you're accessing `app.world` directly, you now have to use
`app.world()` and `app.world_mut()`.
- `App::sub_app` now returns `&SubApp`.
- `App::sub_app_mut`  now returns `&mut SubApp`.
- `App::get_sub_app` now returns `Option<&SubApp>.`
- `App::get_sub_app_mut` now returns `Option<&mut SubApp>.`
This commit is contained in:
Cameron 2024-03-30 20:16:10 -07:00 committed by GitHub
parent ec7755dcce
commit 01649f13e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
86 changed files with 1361 additions and 940 deletions

View file

@ -127,19 +127,19 @@ pub fn empty_schedule_run(criterion: &mut Criterion) {
let mut schedule = Schedule::default();
schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::SingleThreaded);
group.bench_function("SingleThreaded", |bencher| {
bencher.iter(|| schedule.run(&mut app.world));
bencher.iter(|| schedule.run(app.world_mut()));
});
let mut schedule = Schedule::default();
schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::MultiThreaded);
group.bench_function("MultiThreaded", |bencher| {
bencher.iter(|| schedule.run(&mut app.world));
bencher.iter(|| schedule.run(app.world_mut()));
});
let mut schedule = Schedule::default();
schedule.set_executor_kind(bevy_ecs::schedule::ExecutorKind::Simple);
group.bench_function("Simple", |bencher| {
bencher.iter(|| schedule.run(&mut app.world));
bencher.iter(|| schedule.run(app.world_mut()));
});
group.finish();
}

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,7 @@ mod panic_handler;
mod plugin;
mod plugin_group;
mod schedule_runner;
mod sub_app;
pub use app::*;
pub use bevy_derive::DynamicPlugin;
@ -21,6 +22,7 @@ pub use panic_handler::*;
pub use plugin::*;
pub use plugin_group::*;
pub use schedule_runner::*;
pub use sub_app::*;
#[allow(missing_docs)]
pub mod prelude {
@ -32,6 +34,7 @@ pub mod prelude {
PostStartup, PostUpdate, PreStartup, PreUpdate, SpawnScene, Startup, StateTransition,
Update,
},
sub_app::SubApp,
DynamicPlugin, Plugin, PluginGroup,
};
}

View file

@ -27,13 +27,14 @@ use bevy_ecs::{
/// # Rendering
///
/// Note rendering is not executed in the main schedule by default.
/// Instead, rendering is performed in a separate [`SubApp`](crate::app::SubApp)
/// Instead, rendering is performed in a separate [`SubApp`]
/// which exchanges data with the main app in between the main schedule runs.
///
/// See [`RenderPlugin`] and [`PipelinedRenderingPlugin`] for more details.
///
/// [`RenderPlugin`]: https://docs.rs/bevy/latest/bevy/render/struct.RenderPlugin.html
/// [`PipelinedRenderingPlugin`]: https://docs.rs/bevy/latest/bevy/render/pipelined_rendering/struct.PipelinedRenderingPlugin.html
/// [`SubApp`]: crate::SubApp
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Main;

View file

@ -100,6 +100,26 @@ impl<T: Fn(&mut App) + Send + Sync + 'static> Plugin for T {
}
}
/// Plugins state in the application
#[derive(PartialEq, Eq, Debug, Clone, Copy, PartialOrd, Ord)]
pub enum PluginsState {
/// Plugins are being added.
Adding,
/// All plugins already added are ready.
Ready,
/// Finish has been executed for all plugins added.
Finished,
/// Cleanup has been executed for all plugins added.
Cleaned,
}
/// A dummy plugin that's to temporarily occupy an entry in an app's plugin registry.
pub(crate) struct PlaceholderPlugin;
impl Plugin for PlaceholderPlugin {
fn build(&self, _app: &mut App) {}
}
/// A type representing an unsafe function that returns a mutable pointer to a [`Plugin`].
/// It is used for dynamically loading plugins.
///

View file

@ -94,7 +94,7 @@ impl Plugin for ScheduleRunnerPlugin {
app.update();
if let Some(app_exit_events) =
app.world.get_resource_mut::<Events<AppExit>>()
app.world_mut().get_resource_mut::<Events<AppExit>>()
{
if let Some(exit) = app_exit_event_reader.read(&app_exit_events).last()
{

View file

@ -0,0 +1,517 @@
use crate::{App, First, InternedAppLabel, Plugin, Plugins, PluginsState, StateTransition};
use bevy_ecs::{
prelude::*,
schedule::{
common_conditions::run_once as run_once_condition, run_enter_schedule,
InternedScheduleLabel, ScheduleBuildSettings, ScheduleLabel,
},
system::SystemId,
};
#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;
use bevy_utils::{default, HashMap, HashSet};
use std::fmt::Debug;
type ExtractFn = Box<dyn Fn(&mut World, &mut World) + Send>;
#[derive(Default)]
pub(crate) struct PluginStore {
pub(crate) registry: Vec<Box<dyn Plugin>>,
pub(crate) names: HashSet<String>,
}
/// A secondary application with its own [`World`]. These can run independently of each other.
///
/// These are useful for situations where certain processes (e.g. a render thread) need to be kept
/// separate from the main application.
///
/// # Example
///
/// ```
/// # use bevy_app::{App, AppLabel, SubApp, Main};
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::schedule::ScheduleLabel;
///
/// #[derive(Resource, Default)]
/// struct Val(pub i32);
///
/// #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
/// struct ExampleApp;
///
/// // Create an app with a certain resource.
/// let mut app = App::new();
/// app.insert_resource(Val(10));
///
/// // Create a sub-app with the same resource and a single schedule.
/// let mut sub_app = SubApp::new();
/// sub_app.insert_resource(Val(100));
///
/// // Setup an extract function to copy the resource's value in the main world.
/// sub_app.set_extract(|main_world, sub_world| {
/// sub_world.resource_mut::<Val>().0 = main_world.resource::<Val>().0;
/// });
///
/// // Schedule a system that will verify extraction is working.
/// sub_app.add_systems(Main, |counter: Res<Val>| {
/// // The value will be copied during extraction, so we should see 10 instead of 100.
/// assert_eq!(counter.0, 10);
/// });
///
/// // Add the sub-app to the main app.
/// app.insert_sub_app(ExampleApp, sub_app);
///
/// // Update the application once (using the default runner).
/// app.run();
/// ```
pub struct SubApp {
/// The data of this application.
world: World,
/// Metadata for installed plugins.
pub(crate) plugins: PluginStore,
/// Panics if an update is attempted while plugins are building.
pub(crate) plugin_build_depth: usize,
pub(crate) plugins_state: PluginsState,
/// The schedule that will be run by [`update`](Self::update).
pub update_schedule: Option<InternedScheduleLabel>,
/// A function that gives mutable access to two app worlds. This is primarily
/// intended for copying data from the main world to secondary worlds.
extract: Option<ExtractFn>,
}
impl Debug for SubApp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SubApp")
}
}
impl Default for SubApp {
fn default() -> Self {
let mut world = World::new();
world.init_resource::<Schedules>();
Self {
world,
plugins: default(),
plugin_build_depth: 0,
plugins_state: PluginsState::Adding,
update_schedule: None,
extract: None,
}
}
}
impl SubApp {
/// Returns a default, empty [`SubApp`].
pub fn new() -> Self {
Self::default()
}
/// This method is a workaround. Each [`SubApp`] can have its own plugins, but [`Plugin`]
/// works on an [`App`] as a whole.
fn run_as_app<F>(&mut self, f: F)
where
F: FnOnce(&mut App),
{
let mut app = App::empty();
std::mem::swap(self, &mut app.sub_apps.main);
f(&mut app);
std::mem::swap(self, &mut app.sub_apps.main);
}
/// Returns a reference to the [`World`].
pub fn world(&self) -> &World {
&self.world
}
/// Returns a mutable reference to the [`World`].
pub fn world_mut(&mut self) -> &mut World {
&mut self.world
}
/// Runs the default schedule.
pub fn update(&mut self) {
if self.is_building_plugins() {
panic!("SubApp::update() was called while a plugin was building.");
}
if let Some(label) = self.update_schedule {
self.world.run_schedule(label);
}
self.world.clear_trackers();
}
/// Extracts data from `world` into the app's world using the registered extract method.
///
/// **Note:** There is no default extract method. Calling `extract` does nothing if
/// [`set_extract`](Self::set_extract) has not been called.
pub fn extract(&mut self, world: &mut World) {
if let Some(f) = self.extract.as_mut() {
f(world, &mut self.world);
}
}
/// Sets the method that will be called by [`extract`](Self::extract).
///
/// The first argument is the `World` to extract data from, the second argument is the app `World`.
pub fn set_extract<F>(&mut self, extract: F) -> &mut Self
where
F: Fn(&mut World, &mut World) + Send + 'static,
{
self.extract = Some(Box::new(extract));
self
}
/// See [`App::insert_resource`].
pub fn insert_resource<R: Resource>(&mut self, resource: R) -> &mut Self {
self.world.insert_resource(resource);
self
}
/// See [`App::init_resource`].
pub fn init_resource<R: Resource + FromWorld>(&mut self) -> &mut Self {
self.world.init_resource::<R>();
self
}
/// See [`App::add_systems`].
pub fn add_systems<M>(
&mut self,
schedule: impl ScheduleLabel,
systems: impl IntoSystemConfigs<M>,
) -> &mut Self {
let label = schedule.intern();
let mut schedules = self.world.resource_mut::<Schedules>();
if let Some(schedule) = schedules.get_mut(label) {
schedule.add_systems(systems);
} else {
let mut new_schedule = Schedule::new(label);
new_schedule.add_systems(systems);
schedules.insert(new_schedule);
}
self
}
/// See [`App::register_system`].
pub fn register_system<I: 'static, O: 'static, M, S: IntoSystem<I, O, M> + 'static>(
&mut self,
system: S,
) -> SystemId<I, O> {
self.world.register_system(system)
}
/// See [`App::configure_sets`].
#[track_caller]
pub fn configure_sets(
&mut self,
schedule: impl ScheduleLabel,
sets: impl IntoSystemSetConfigs,
) -> &mut Self {
let label = schedule.intern();
let mut schedules = self.world.resource_mut::<Schedules>();
if let Some(schedule) = schedules.get_mut(label) {
schedule.configure_sets(sets);
} else {
let mut new_schedule = Schedule::new(label);
new_schedule.configure_sets(sets);
schedules.insert(new_schedule);
}
self
}
/// See [`App::add_schedule`].
pub fn add_schedule(&mut self, schedule: Schedule) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>();
schedules.insert(schedule);
self
}
/// See [`App::init_schedule`].
pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self {
let label = label.intern();
let mut schedules = self.world.resource_mut::<Schedules>();
if !schedules.contains(label) {
schedules.insert(Schedule::new(label));
}
self
}
/// See [`App::get_schedule`].
pub fn get_schedule(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
let schedules = self.world.get_resource::<Schedules>()?;
schedules.get(label)
}
/// See [`App::get_schedule_mut`].
pub fn get_schedule_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
let schedules = self.world.get_resource_mut::<Schedules>()?;
// We must call `.into_inner` here because the borrow checker only understands reborrows
// using ordinary references, not our `Mut` smart pointers.
schedules.into_inner().get_mut(label)
}
/// See [`App::edit_schedule`].
pub fn edit_schedule(
&mut self,
label: impl ScheduleLabel,
mut f: impl FnMut(&mut Schedule),
) -> &mut Self {
let label = label.intern();
let mut schedules = self.world.resource_mut::<Schedules>();
if !schedules.contains(label) {
schedules.insert(Schedule::new(label));
}
let schedule = schedules.get_mut(label).unwrap();
f(schedule);
self
}
/// See [`App::configure_schedules`].
pub fn configure_schedules(
&mut self,
schedule_build_settings: ScheduleBuildSettings,
) -> &mut Self {
self.world_mut()
.resource_mut::<Schedules>()
.configure_schedules(schedule_build_settings);
self
}
/// See [`App::allow_ambiguous_component`].
pub fn allow_ambiguous_component<T: Component>(&mut self) -> &mut Self {
self.world_mut().allow_ambiguous_component::<T>();
self
}
/// See [`App::allow_ambiguous_resource`].
pub fn allow_ambiguous_resource<T: Resource>(&mut self) -> &mut Self {
self.world_mut().allow_ambiguous_resource::<T>();
self
}
/// See [`App::ignore_ambiguity`].
#[track_caller]
pub fn ignore_ambiguity<M1, M2, S1, S2>(
&mut self,
schedule: impl ScheduleLabel,
a: S1,
b: S2,
) -> &mut Self
where
S1: IntoSystemSet<M1>,
S2: IntoSystemSet<M2>,
{
let schedule = schedule.intern();
let mut schedules = self.world.resource_mut::<Schedules>();
if let Some(schedule) = schedules.get_mut(schedule) {
let schedule: &mut Schedule = schedule;
schedule.ignore_ambiguity(a, b);
} else {
let mut new_schedule = Schedule::new(schedule);
new_schedule.ignore_ambiguity(a, b);
schedules.insert(new_schedule);
}
self
}
/// See [`App::init_state`].
pub fn init_state<S: States + FromWorld>(&mut self) -> &mut Self {
if !self.world.contains_resource::<State<S>>() {
self.init_resource::<State<S>>()
.init_resource::<NextState<S>>()
.add_event::<StateTransitionEvent<S>>()
.add_systems(
StateTransition,
(
run_enter_schedule::<S>.run_if(run_once_condition()),
apply_state_transition::<S>,
)
.chain(),
);
}
// The OnEnter, OnExit, and OnTransition schedules are lazily initialized
// (i.e. when the first system is added to them), so World::try_run_schedule
// is used to fail gracefully if they aren't present.
self
}
/// See [`App::insert_state`].
pub fn insert_state<S: States>(&mut self, state: S) -> &mut Self {
self.insert_resource(State::new(state))
.init_resource::<NextState<S>>()
.add_event::<StateTransitionEvent<S>>()
.add_systems(
StateTransition,
(
run_enter_schedule::<S>.run_if(run_once_condition()),
apply_state_transition::<S>,
)
.chain(),
);
self
}
/// See [`App::add_event`].
pub fn add_event<T>(&mut self) -> &mut Self
where
T: Event,
{
if !self.world.contains_resource::<Events<T>>() {
self.init_resource::<Events<T>>().add_systems(
First,
bevy_ecs::event::event_update_system::<T>
.in_set(bevy_ecs::event::EventUpdates)
.run_if(bevy_ecs::event::event_update_condition::<T>),
);
}
self
}
/// See [`App::add_plugins`].
pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
self.run_as_app(|app| plugins.add_to_app(app));
self
}
/// See [`App::is_plugin_added`].
pub fn is_plugin_added<T>(&self) -> bool
where
T: Plugin,
{
self.plugins
.registry
.iter()
.any(|p| p.downcast_ref::<T>().is_some())
}
/// See [`App::get_added_plugins`].
pub fn get_added_plugins<T>(&self) -> Vec<&T>
where
T: Plugin,
{
self.plugins
.registry
.iter()
.filter_map(|p| p.downcast_ref())
.collect()
}
/// Returns `true` if there is no plugin in the middle of being built.
pub(crate) fn is_building_plugins(&self) -> bool {
self.plugin_build_depth > 0
}
/// Return the state of plugins.
#[inline]
pub fn plugins_state(&mut self) -> PluginsState {
match self.plugins_state {
PluginsState::Adding => {
let mut state = PluginsState::Ready;
let plugins = std::mem::take(&mut self.plugins);
self.run_as_app(|app| {
for plugin in &plugins.registry {
if !plugin.ready(app) {
state = PluginsState::Adding;
return;
}
}
});
self.plugins = plugins;
state
}
state => state,
}
}
/// Runs [`Plugin::finish`] for each plugin.
pub fn finish(&mut self) {
let plugins = std::mem::take(&mut self.plugins);
self.run_as_app(|app| {
for plugin in &plugins.registry {
plugin.finish(app);
}
});
self.plugins = plugins;
self.plugins_state = PluginsState::Finished;
}
/// Runs [`Plugin::cleanup`] for each plugin.
pub fn cleanup(&mut self) {
let plugins = std::mem::take(&mut self.plugins);
self.run_as_app(|app| {
for plugin in &plugins.registry {
plugin.cleanup(app);
}
});
self.plugins = plugins;
self.plugins_state = PluginsState::Cleaned;
}
/// See [`App::register_type`].
#[cfg(feature = "bevy_reflect")]
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
let registry = self.world.resource_mut::<AppTypeRegistry>();
registry.write().register::<T>();
self
}
/// See [`App::register_type_data`].
#[cfg(feature = "bevy_reflect")]
pub fn register_type_data<
T: bevy_reflect::Reflect + bevy_reflect::TypePath,
D: bevy_reflect::TypeData + bevy_reflect::FromType<T>,
>(
&mut self,
) -> &mut Self {
let registry = self.world.resource_mut::<AppTypeRegistry>();
registry.write().register_type_data::<T, D>();
self
}
}
/// The collection of sub-apps that belong to an [`App`].
#[derive(Default)]
pub struct SubApps {
/// The primary sub-app that contains the "main" world.
pub main: SubApp,
/// Other, labeled sub-apps.
pub sub_apps: HashMap<InternedAppLabel, SubApp>,
}
impl SubApps {
/// Calls [`update`](SubApp::update) for the main sub-app, and then calls
/// [`extract`](SubApp::extract) and [`update`](SubApp::update) for the rest.
pub fn update(&mut self) {
#[cfg(feature = "trace")]
let _bevy_update_span = info_span!("update").entered();
{
#[cfg(feature = "trace")]
let _bevy_frame_update_span = info_span!("main app").entered();
self.main.update();
}
for (_label, sub_app) in self.sub_apps.iter_mut() {
#[cfg(feature = "trace")]
let _sub_app_span = info_span!("sub app", name = ?_label).entered();
sub_app.extract(&mut self.main.world);
sub_app.update();
}
self.main.world.clear_trackers();
}
/// Returns an iterator over the sub-apps (starting with the main one).
pub fn iter(&self) -> impl Iterator<Item = &SubApp> + '_ {
std::iter::once(&self.main).chain(self.sub_apps.values())
}
/// Returns a mutable iterator over the sub-apps (starting with the main one).
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut SubApp> + '_ {
std::iter::once(&mut self.main).chain(self.sub_apps.values_mut())
}
}

View file

@ -224,7 +224,7 @@ macro_rules! embedded_asset {
($app: ident, $source_path: expr, $path: expr) => {{
let mut embedded = $app
.world
.world_mut()
.resource_mut::<$crate::io::embedded::EmbeddedAssetRegistry>();
let path = $crate::embedded_path!($source_path, $path);
let watched_path = $crate::io::embedded::watched_path(file!(), $path);
@ -253,7 +253,7 @@ pub fn watched_path(_source_file_path: &'static str, _asset_path: &'static str)
#[macro_export]
macro_rules! load_internal_asset {
($app: ident, $handle: expr, $path_str: expr, $loader: expr) => {{
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
let mut assets = $app.world_mut().resource_mut::<$crate::Assets<_>>();
assets.insert($handle.id(), ($loader)(
include_str!($path_str),
std::path::Path::new(file!())
@ -265,7 +265,7 @@ macro_rules! load_internal_asset {
}};
// we can't support params without variadic arguments, so internal assets with additional params can't be hot-reloaded
($app: ident, $handle: ident, $path_str: expr, $loader: expr $(, $param:expr)+) => {{
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
let mut assets = $app.world_mut().resource_mut::<$crate::Assets<_>>();
assets.insert($handle.id(), ($loader)(
include_str!($path_str),
std::path::Path::new(file!())
@ -282,7 +282,7 @@ macro_rules! load_internal_asset {
#[macro_export]
macro_rules! load_internal_binary_asset {
($app: ident, $handle: expr, $path_str: expr, $loader: expr) => {{
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
let mut assets = $app.world_mut().resource_mut::<$crate::Assets<_>>();
assets.insert(
$handle.id(),
($loader)(

View file

@ -153,7 +153,7 @@ impl Plugin for AssetPlugin {
let embedded = EmbeddedAssetRegistry::default();
{
let mut sources = app
.world
.world_mut()
.get_resource_or_insert_with::<AssetSourceBuilders>(Default::default);
sources.init_default_source(
&self.file_path,
@ -169,10 +169,10 @@ impl Plugin for AssetPlugin {
}
match self.mode {
AssetMode::Unprocessed => {
let mut builders = app.world.resource_mut::<AssetSourceBuilders>();
let mut builders = app.world_mut().resource_mut::<AssetSourceBuilders>();
let sources = builders.build_sources(watch, false);
let meta_check = app
.world
.world()
.get_resource::<AssetMetaCheck>()
.cloned()
.unwrap_or_else(AssetMetaCheck::default);
@ -187,7 +187,7 @@ impl Plugin for AssetPlugin {
AssetMode::Processed => {
#[cfg(feature = "asset_processor")]
{
let mut builders = app.world.resource_mut::<AssetSourceBuilders>();
let mut builders = app.world_mut().resource_mut::<AssetSourceBuilders>();
let processor = AssetProcessor::new(&mut builders);
let mut sources = builders.build_sources(false, watch);
sources.gate_on_processor(processor.data.clone());
@ -204,7 +204,7 @@ impl Plugin for AssetPlugin {
}
#[cfg(not(feature = "asset_processor"))]
{
let mut builders = app.world.resource_mut::<AssetSourceBuilders>();
let mut builders = app.world_mut().resource_mut::<AssetSourceBuilders>();
let sources = builders.build_sources(false, watch);
app.insert_resource(AssetServer::new_with_meta_check(
sources,
@ -318,12 +318,14 @@ pub trait AssetApp {
impl AssetApp for App {
fn register_asset_loader<L: AssetLoader>(&mut self, loader: L) -> &mut Self {
self.world.resource::<AssetServer>().register_loader(loader);
self.world()
.resource::<AssetServer>()
.register_loader(loader);
self
}
fn register_asset_processor<P: Process>(&mut self, processor: P) -> &mut Self {
if let Some(asset_processor) = self.world.get_resource::<AssetProcessor>() {
if let Some(asset_processor) = self.world().get_resource::<AssetProcessor>() {
asset_processor.register_processor(processor);
}
self
@ -335,13 +337,13 @@ impl AssetApp for App {
source: AssetSourceBuilder,
) -> &mut Self {
let id = id.into();
if self.world.get_resource::<AssetServer>().is_some() {
if self.world().get_resource::<AssetServer>().is_some() {
error!("{} must be registered before `AssetPlugin` (typically added as part of `DefaultPlugins`)", id);
}
{
let mut sources = self
.world
.world_mut()
.get_resource_or_insert_with(AssetSourceBuilders::default);
sources.insert(id, source);
}
@ -350,22 +352,24 @@ impl AssetApp for App {
}
fn set_default_asset_processor<P: Process>(&mut self, extension: &str) -> &mut Self {
if let Some(asset_processor) = self.world.get_resource::<AssetProcessor>() {
if let Some(asset_processor) = self.world().get_resource::<AssetProcessor>() {
asset_processor.set_default_processor::<P>(extension);
}
self
}
fn init_asset_loader<L: AssetLoader + FromWorld>(&mut self) -> &mut Self {
let loader = L::from_world(&mut self.world);
let loader = L::from_world(self.world_mut());
self.register_asset_loader(loader)
}
fn init_asset<A: Asset>(&mut self) -> &mut Self {
let assets = Assets::<A>::default();
self.world.resource::<AssetServer>().register_asset(&assets);
if self.world.contains_resource::<AssetProcessor>() {
let processor = self.world.resource::<AssetProcessor>();
self.world()
.resource::<AssetServer>()
.register_asset(&assets);
if self.world().contains_resource::<AssetProcessor>() {
let processor = self.world().resource::<AssetProcessor>();
// The processor should have its own handle provider separate from the Asset storage
// to ensure the id spaces are entirely separate. Not _strictly_ necessary, but
// desirable.
@ -394,7 +398,7 @@ impl AssetApp for App {
where
A: Asset + Reflect + FromReflect + GetTypeRegistration,
{
let type_registry = self.world.resource::<AppTypeRegistry>();
let type_registry = self.world().resource::<AppTypeRegistry>();
{
let mut type_registry = type_registry.write();
@ -408,7 +412,7 @@ impl AssetApp for App {
}
fn preregister_asset_loader<L: AssetLoader>(&mut self, extensions: &[&str]) -> &mut Self {
self.world
self.world_mut()
.resource_mut::<AssetServer>()
.preregister_loader::<L>(extensions);
self
@ -628,7 +632,7 @@ mod tests {
pub fn run_app_until(app: &mut App, mut predicate: impl FnMut(&mut World) -> Option<()>) {
for _ in 0..LARGE_ITERATION_COUNT {
app.update();
if predicate(&mut app.world).is_some() {
if predicate(app.world_mut()).is_some() {
return;
}
}
@ -718,13 +722,13 @@ mod tests {
.init_resource::<StoredEvents>()
.register_asset_loader(CoolTextLoader)
.add_systems(Update, store_asset_events);
let asset_server = app.world.resource::<AssetServer>().clone();
let asset_server = app.world().resource::<AssetServer>().clone();
let handle: Handle<CoolText> = asset_server.load(a_path);
let a_id = handle.id();
let entity = app.world.spawn(handle).id();
let entity = app.world_mut().spawn(handle).id();
app.update();
{
let a_text = get::<CoolText>(&app.world, a_id);
let a_text = get::<CoolText>(app.world(), a_id);
let (a_load, a_deps, a_rec_deps) = asset_server.get_load_states(a_id).unwrap();
assert!(a_text.is_none(), "a's asset should not exist yet");
assert_eq!(a_load, LoadState::Loading, "a should still be loading");
@ -906,27 +910,27 @@ mod tests {
});
{
let mut texts = app.world.resource_mut::<Assets<CoolText>>();
let mut texts = app.world_mut().resource_mut::<Assets<CoolText>>();
let a = texts.get_mut(a_id).unwrap();
a.text = "Changed".to_string();
}
app.world.despawn(entity);
app.world_mut().despawn(entity);
app.update();
assert_eq!(
app.world.resource::<Assets<CoolText>>().len(),
app.world().resource::<Assets<CoolText>>().len(),
0,
"CoolText asset entities should be despawned when no more handles exist"
);
app.update();
// this requires a second update because the parent asset was freed in the previous app.update()
assert_eq!(
app.world.resource::<Assets<SubText>>().len(),
app.world().resource::<Assets<SubText>>().len(),
0,
"SubText asset entities should be despawned when no more handles exist"
);
let events = app.world.remove_resource::<StoredEvents>().unwrap();
let id_results = app.world.remove_resource::<IdResults>().unwrap();
let events = app.world_mut().remove_resource::<StoredEvents>().unwrap();
let id_results = app.world_mut().remove_resource::<IdResults>().unwrap();
let expected_events = vec![
AssetEvent::Added { id: a_id },
AssetEvent::LoadedWithDependencies {
@ -1030,7 +1034,7 @@ mod tests {
let (mut app, gate_opener) = test_app(dir);
app.init_asset::<CoolText>()
.register_asset_loader(CoolTextLoader);
let asset_server = app.world.resource::<AssetServer>().clone();
let asset_server = app.world().resource::<AssetServer>().clone();
let handle: Handle<CoolText> = asset_server.load(a_path);
let a_id = handle.id();
{
@ -1046,7 +1050,7 @@ mod tests {
);
}
app.world.spawn(handle);
app.world_mut().spawn(handle);
gate_opener.open(a_path);
gate_opener.open(b_path);
gate_opener.open(c_path);
@ -1117,7 +1121,7 @@ mod tests {
let id = {
let handle = {
let mut texts = app.world.resource_mut::<Assets<CoolText>>();
let mut texts = app.world_mut().resource_mut::<Assets<CoolText>>();
let handle = texts.add(CoolText::default());
texts.get_strong_handle(handle.id()).unwrap()
};
@ -1125,7 +1129,7 @@ mod tests {
app.update();
{
let text = app.world.resource::<Assets<CoolText>>().get(&handle);
let text = app.world().resource::<Assets<CoolText>>().get(&handle);
assert!(text.is_some());
}
handle.id()
@ -1133,7 +1137,7 @@ mod tests {
// handle is dropped
app.update();
assert!(
app.world.resource::<Assets<CoolText>>().get(id).is_none(),
app.world().resource::<Assets<CoolText>>().get(id).is_none(),
"asset has no handles, so it should have been dropped last update"
);
}
@ -1161,7 +1165,7 @@ mod tests {
let id = {
let handle = {
let mut texts = app.world.resource_mut::<Assets<CoolText>>();
let mut texts = app.world_mut().resource_mut::<Assets<CoolText>>();
texts.add(CoolText {
text: hello.clone(),
embedded: empty.clone(),
@ -1174,7 +1178,7 @@ mod tests {
{
let text = app
.world
.world()
.resource::<Assets<CoolText>>()
.get(&handle)
.unwrap();
@ -1185,12 +1189,12 @@ mod tests {
// handle is dropped
app.update();
assert!(
app.world.resource::<Assets<CoolText>>().get(id).is_none(),
app.world().resource::<Assets<CoolText>>().get(id).is_none(),
"asset has no handles, so it should have been dropped last update"
);
// remove event is emitted
app.update();
let events = std::mem::take(&mut app.world.resource_mut::<StoredEvents>().0);
let events = std::mem::take(&mut app.world_mut().resource_mut::<StoredEvents>().0);
let expected_events = vec![
AssetEvent::Added { id },
AssetEvent::Unused { id },
@ -1198,7 +1202,7 @@ mod tests {
];
assert_eq!(events, expected_events);
let dep_handle = app.world.resource::<AssetServer>().load(dep_path);
let dep_handle = app.world().resource::<AssetServer>().load(dep_path);
let a = CoolText {
text: "a".to_string(),
embedded: empty,
@ -1206,19 +1210,19 @@ mod tests {
dependencies: vec![dep_handle.clone()],
sub_texts: Vec::new(),
};
let a_handle = app.world.resource::<AssetServer>().load_asset(a);
let a_handle = app.world().resource::<AssetServer>().load_asset(a);
app.update();
// TODO: ideally it doesn't take two updates for the added event to emit
app.update();
let events = std::mem::take(&mut app.world.resource_mut::<StoredEvents>().0);
let events = std::mem::take(&mut app.world_mut().resource_mut::<StoredEvents>().0);
let expected_events = vec![AssetEvent::Added { id: a_handle.id() }];
assert_eq!(events, expected_events);
gate_opener.open(dep_path);
loop {
app.update();
let events = std::mem::take(&mut app.world.resource_mut::<StoredEvents>().0);
let events = std::mem::take(&mut app.world_mut().resource_mut::<StoredEvents>().0);
if events.is_empty() {
continue;
}
@ -1232,7 +1236,7 @@ mod tests {
break;
}
app.update();
let events = std::mem::take(&mut app.world.resource_mut::<StoredEvents>().0);
let events = std::mem::take(&mut app.world_mut().resource_mut::<StoredEvents>().0);
let expected_events = vec![AssetEvent::Added {
id: dep_handle.id(),
}];
@ -1283,7 +1287,7 @@ mod tests {
app.init_asset::<CoolText>()
.init_asset::<SubText>()
.register_asset_loader(CoolTextLoader);
let asset_server = app.world.resource::<AssetServer>().clone();
let asset_server = app.world().resource::<AssetServer>().clone();
let handle: Handle<LoadedFolder> = asset_server.load_folder("text");
gate_opener.open(a_path);
gate_opener.open(b_path);
@ -1438,12 +1442,12 @@ mod tests {
(asset_event_handler, asset_load_error_event_handler).chain(),
);
let asset_server = app.world.resource::<AssetServer>().clone();
let asset_server = app.world().resource::<AssetServer>().clone();
let a_path = format!("unstable://{a_path}");
let a_handle: Handle<CoolText> = asset_server.load(a_path);
let a_id = a_handle.id();
app.world.spawn(a_handle);
app.world_mut().spawn(a_handle);
run_app_until(&mut app, |world| {
let tracker = world.resource::<ErrorTracker>();
@ -1476,7 +1480,7 @@ mod tests {
});
// running schedule does not error on ambiguity between the 2 uses_assets systems
app.world.run_schedule(Update);
app.world_mut().run_schedule(Update);
}
// validate the Asset derive macro for various asset types

View file

@ -257,7 +257,7 @@ mod tests {
.register_asset_reflect::<AssetType>();
let reflect_asset = {
let type_registry = app.world.resource::<AppTypeRegistry>();
let type_registry = app.world().resource::<AppTypeRegistry>();
let type_registry = type_registry.read();
type_registry
@ -270,9 +270,9 @@ mod tests {
field: "test".into(),
};
let handle = reflect_asset.add(&mut app.world, &value);
let handle = reflect_asset.add(app.world_mut(), &value);
let ReflectMut::Struct(strukt) = reflect_asset
.get_mut(&mut app.world, handle)
.get_mut(app.world_mut(), handle)
.unwrap()
.reflect_mut()
else {
@ -283,19 +283,19 @@ mod tests {
.unwrap()
.apply(&String::from("edited"));
assert_eq!(reflect_asset.len(&app.world), 1);
let ids: Vec<_> = reflect_asset.ids(&app.world).collect();
assert_eq!(reflect_asset.len(app.world()), 1);
let ids: Vec<_> = reflect_asset.ids(app.world()).collect();
assert_eq!(ids.len(), 1);
let fetched_handle = UntypedHandle::Weak(ids[0]);
let asset = reflect_asset
.get(&app.world, fetched_handle.clone_weak())
.get(app.world(), fetched_handle.clone_weak())
.unwrap();
assert_eq!(asset.downcast_ref::<AssetType>().unwrap().field, "edited");
reflect_asset
.remove(&mut app.world, fetched_handle)
.remove(app.world_mut(), fetched_handle)
.unwrap();
assert_eq!(reflect_asset.len(&app.world), 0);
assert_eq!(reflect_asset.len(app.world()), 0);
}
}

View file

@ -149,7 +149,7 @@ mod tests {
));
app.update();
let frame_count = app.world.resource::<FrameCount>();
let frame_count = app.world().resource::<FrameCount>();
assert_eq!(1, frame_count.0);
}
}

View file

@ -21,16 +21,15 @@ impl Plugin for BlitPlugin {
fn build(&self, app: &mut App) {
load_internal_asset!(app, BLIT_SHADER_HANDLE, "blit.wgsl", Shader::from_wgsl);
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.allow_ambiguous_resource::<SpecializedRenderPipelines<BlitPipeline>>();
}
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.init_resource::<BlitPipeline>()
.init_resource::<SpecializedRenderPipelines<BlitPipeline>>();

View file

@ -56,10 +56,9 @@ impl Plugin for BloomPlugin {
UniformComponentPlugin::<BloomUniforms>::default(),
));
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.init_resource::<SpecializedRenderPipelines<BloomDownsamplingPipeline>>()
.init_resource::<SpecializedRenderPipelines<BloomUpsamplingPipeline>>()
@ -87,10 +86,9 @@ impl Plugin for BloomPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.init_resource::<BloomDownsamplingPipeline>()
.init_resource::<BloomUpsamplingPipeline>();

View file

@ -116,7 +116,7 @@ impl Plugin for CASPlugin {
UniformComponentPlugin::<CASUniform>::default(),
));
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
@ -160,7 +160,7 @@ impl Plugin for CASPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.init_resource::<CASPipeline>();

View file

@ -56,10 +56,9 @@ impl Plugin for Core2dPlugin {
app.register_type::<Camera2d>()
.add_plugins(ExtractComponentPlugin::<Camera2d>::default());
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.init_resource::<DrawFunctions<Transparent2d>>()
.add_systems(ExtractSchedule, extract_core_2d_camera_phases)

View file

@ -99,10 +99,9 @@ impl Plugin for Core3dPlugin {
.add_plugins((SkyboxPlugin, ExtractComponentPlugin::<Camera3d>::default()))
.add_systems(PostUpdate, check_msaa);
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.init_resource::<DrawFunctions<Opaque3d>>()
.init_resource::<DrawFunctions<AlphaMask3d>>()

View file

@ -35,7 +35,7 @@ impl Plugin for CopyDeferredLightingIdPlugin {
"copy_deferred_lighting_id.wgsl",
Shader::from_wgsl
);
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.add_systems(
@ -45,7 +45,7 @@ impl Plugin for CopyDeferredLightingIdPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -89,7 +89,7 @@ impl Plugin for FxaaPlugin {
app.register_type::<Fxaa>();
app.add_plugins(ExtractComponentPlugin::<Fxaa>::default());
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
@ -116,7 +116,7 @@ impl Plugin for FxaaPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.init_resource::<FxaaPipeline>();

View file

@ -21,10 +21,9 @@ pub struct MsaaWritebackPlugin;
impl Plugin for MsaaWritebackPlugin {
fn build(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.add_systems(
Render,
prepare_msaa_writeback_pipelines.in_set(RenderSet::Prepare),

View file

@ -38,10 +38,9 @@ impl Plugin for SkyboxPlugin {
UniformComponentPlugin::<SkyboxUniforms>::default(),
));
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.init_resource::<SpecializedRenderPipelines<SkyboxPipeline>>()
.add_systems(
@ -54,12 +53,10 @@ impl Plugin for SkyboxPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
let render_device = render_app.world.resource::<RenderDevice>().clone();
let render_device = render_app.world().resource::<RenderDevice>().clone();
render_app.insert_resource(SkyboxPipeline::new(&render_device));
}
}

View file

@ -49,10 +49,9 @@ impl Plugin for TemporalAntiAliasPlugin {
app.insert_resource(Msaa::Off)
.register_type::<TemporalAntiAliasSettings>();
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.init_resource::<SpecializedRenderPipelines<TaaPipeline>>()
.add_systems(ExtractSchedule, extract_taa_settings)
@ -77,7 +76,7 @@ impl Plugin for TemporalAntiAliasPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -52,8 +52,8 @@ impl Plugin for TonemappingPlugin {
Shader::from_wgsl
);
if !app.world.is_resource_added::<TonemappingLuts>() {
let mut images = app.world.resource_mut::<Assets<Image>>();
if !app.world().is_resource_added::<TonemappingLuts>() {
let mut images = app.world_mut().resource_mut::<Assets<Image>>();
#[cfg(feature = "tonemapping_luts")]
let tonemapping_luts = {
@ -96,20 +96,22 @@ impl Plugin for TonemappingPlugin {
ExtractComponentPlugin::<DebandDither>::default(),
));
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<SpecializedRenderPipelines<TonemappingPipeline>>()
.add_systems(
Render,
prepare_view_tonemapping_pipelines.in_set(RenderSet::Prepare),
);
}
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app
.init_resource::<SpecializedRenderPipelines<TonemappingPipeline>>()
.add_systems(
Render,
prepare_view_tonemapping_pipelines.in_set(RenderSet::Prepare),
);
}
fn finish(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.init_resource::<TonemappingPipeline>();
}
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
render_app.init_resource::<TonemappingPipeline>();
}
}

View file

@ -13,7 +13,7 @@ pub struct UpscalingPlugin;
impl Plugin for UpscalingPlugin {
fn build(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_systems(
Render,
prepare_view_upscaling_pipelines.in_set(RenderSet::Prepare),

View file

@ -62,7 +62,7 @@ pub(crate) fn setup_app(app: &mut App) -> &mut App {
};
if let Some(frame_time) = config.frame_time {
app.world
app.world_mut()
.insert_resource(TimeUpdateStrategy::ManualDuration(Duration::from_secs_f32(
frame_time,
)));

View file

@ -1,7 +1,7 @@
use std::hash::{Hash, Hasher};
use std::{borrow::Cow, collections::VecDeque};
use bevy_app::App;
use bevy_app::{App, SubApp};
use bevy_ecs::system::{Deferred, Res, Resource, SystemBuffer, SystemParam};
use bevy_utils::{hashbrown::HashMap, Duration, Instant, PassHash};
use const_fnv1a_hash::fnv1a_hash_str_64;
@ -383,10 +383,6 @@ impl SystemBuffer for DiagnosticsBuffer {
/// Extend [`App`] with new `register_diagnostic` function.
pub trait RegisterDiagnostic {
fn register_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self;
}
impl RegisterDiagnostic for App {
/// Register a new [`Diagnostic`] with an [`App`].
///
/// Will initialize a [`DiagnosticsStore`] if it doesn't exist.
@ -402,11 +398,22 @@ impl RegisterDiagnostic for App {
/// .add_plugins(DiagnosticsPlugin)
/// .run();
/// ```
fn register_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self;
}
impl RegisterDiagnostic for SubApp {
fn register_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self {
self.init_resource::<DiagnosticsStore>();
let mut diagnostics = self.world.resource_mut::<DiagnosticsStore>();
let mut diagnostics = self.world_mut().resource_mut::<DiagnosticsStore>();
diagnostics.add(diagnostic);
self
}
}
impl RegisterDiagnostic for App {
fn register_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self {
SubApp::register_diagnostic(self.main_mut(), diagnostic);
self
}
}

View file

@ -136,7 +136,7 @@ impl Plugin for GizmoPlugin {
.add_plugins(AabbGizmoPlugin)
.add_plugins(LightGizmoPlugin);
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
@ -162,11 +162,11 @@ impl Plugin for GizmoPlugin {
}
fn finish(&self, app: &mut bevy_app::App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
let render_device = render_app.world.resource::<RenderDevice>();
let render_device = render_app.world().resource::<RenderDevice>();
let line_layout = render_device.create_bind_group_layout(
"LineGizmoUniform layout",
&BindGroupLayoutEntries::single(
@ -200,12 +200,12 @@ pub trait AppGizmoBuilder {
impl AppGizmoBuilder for App {
fn init_gizmo_group<T: GizmoConfigGroup + Default>(&mut self) -> &mut Self {
if self.world.contains_resource::<GizmoStorage<T>>() {
if self.world().contains_resource::<GizmoStorage<T>>() {
return self;
}
let mut handles = self
.world
.world_mut()
.get_resource_or_insert_with::<LineGizmoHandles>(Default::default);
handles.list.insert(TypeId::of::<T>(), None);
handles.strip.insert(TypeId::of::<T>(), None);
@ -213,7 +213,7 @@ impl AppGizmoBuilder for App {
self.init_resource::<GizmoStorage<T>>()
.add_systems(Last, update_gizmo_meshes::<T>);
self.world
self.world_mut()
.get_resource_or_insert_with::<GizmoConfigStore>(Default::default)
.register::<T>();
@ -225,16 +225,16 @@ impl AppGizmoBuilder for App {
group: T,
config: GizmoConfig,
) -> &mut Self {
self.world
self.world_mut()
.get_resource_or_insert_with::<GizmoConfigStore>(Default::default)
.insert(config, group);
if self.world.contains_resource::<GizmoStorage<T>>() {
if self.world().contains_resource::<GizmoStorage<T>>() {
return self;
}
let mut handles = self
.world
.world_mut()
.get_resource_or_insert_with::<LineGizmoHandles>(Default::default);
handles.list.insert(TypeId::of::<T>(), None);
handles.strip.insert(TypeId::of::<T>(), None);

View file

@ -30,7 +30,7 @@ pub struct LineGizmo2dPlugin;
impl Plugin for LineGizmo2dPlugin {
fn build(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
@ -57,7 +57,7 @@ impl Plugin for LineGizmo2dPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -32,7 +32,7 @@ use bevy_utils::tracing::error;
pub struct LineGizmo3dPlugin;
impl Plugin for LineGizmo3dPlugin {
fn build(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
@ -56,7 +56,7 @@ impl Plugin for LineGizmo3dPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -63,9 +63,8 @@ impl Plugin for GltfPlugin {
}
fn finish(&self, app: &mut App) {
let supported_compressed_formats = match app.world.get_resource::<RenderDevice>() {
let supported_compressed_formats = match app.world().get_resource::<RenderDevice>() {
Some(render_device) => CompressedImageFormats::from_features(render_device.features()),
None => CompressedImageFormats::NONE,
};
app.register_asset_loader(GltfLoader {

View file

@ -177,7 +177,7 @@ impl Plugin for LogPlugin {
}
}))
.build();
app.world.insert_non_send_resource(guard);
app.insert_non_send_resource(guard);
chrome_layer
};

View file

@ -105,7 +105,7 @@ impl Plugin for DeferredPbrLightingPlugin {
Shader::from_wgsl
);
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
@ -130,7 +130,7 @@ impl Plugin for DeferredPbrLightingPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -352,16 +352,18 @@ impl Plugin for PbrPlugin {
app.add_plugins(DeferredPbrLightingPlugin);
}
app.world.resource_mut::<Assets<StandardMaterial>>().insert(
&Handle::<StandardMaterial>::default(),
StandardMaterial {
base_color: Color::srgb(1.0, 0.0, 0.5),
unlit: true,
..Default::default()
},
);
app.world_mut()
.resource_mut::<Assets<StandardMaterial>>()
.insert(
&Handle::<StandardMaterial>::default(),
StandardMaterial {
base_color: Color::srgb(1.0, 0.0, 0.5),
unlit: true,
..Default::default()
},
);
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
@ -379,8 +381,8 @@ impl Plugin for PbrPlugin {
)
.init_resource::<LightMeta>();
let shadow_pass_node = ShadowPassNode::new(&mut render_app.world);
let mut graph = render_app.world.resource_mut::<RenderGraph>();
let shadow_pass_node = ShadowPassNode::new(render_app.world_mut());
let mut graph = render_app.world_mut().resource_mut::<RenderGraph>();
let draw_3d_graph = graph.get_sub_graph_mut(Core3d).unwrap();
draw_3d_graph.add_node(NodePbr::ShadowPass, shadow_pass_node);
draw_3d_graph.add_node_edge(NodePbr::ShadowPass, Node3d::StartMainPass);
@ -396,7 +398,7 @@ impl Plugin for PbrPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -323,7 +323,7 @@ impl Plugin for LightProbePlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -126,7 +126,7 @@ impl Plugin for LightmapPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -253,7 +253,7 @@ where
app.init_asset::<M>()
.add_plugins(ExtractInstancesPlugin::<AssetId<M>>::extract_visible());
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<DrawFunctions<Shadow>>()
.add_render_command::<Shadow, DrawPrepass<M>>()
@ -310,7 +310,7 @@ where
}
fn finish(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.init_resource::<MaterialPipeline<M>>();
}
}

View file

@ -164,7 +164,7 @@ impl Plugin for MeshletPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -90,7 +90,7 @@ where
Shader::from_wgsl
);
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
@ -106,7 +106,7 @@ where
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
@ -130,7 +130,10 @@ where
M::Data: PartialEq + Eq + Hash + Clone,
{
fn build(&self, app: &mut App) {
let no_prepass_plugin_loaded = app.world.get_resource::<AnyPrepassPluginLoaded>().is_none();
let no_prepass_plugin_loaded = app
.world()
.get_resource::<AnyPrepassPluginLoaded>()
.is_none();
if no_prepass_plugin_loaded {
app.insert_resource(AnyPrepassPluginLoaded)
@ -145,7 +148,7 @@ where
);
}
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -135,7 +135,7 @@ impl Plugin for FogPlugin {
app.register_type::<FogSettings>();
app.add_plugins(ExtractComponentPlugin::<FogSettings>::default());
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<FogMeta>()
.add_systems(Render, prepare_fog.in_set(RenderSet::PrepareResources));

View file

@ -109,7 +109,7 @@ impl Plugin for MeshRenderPlugin {
(no_automatic_skin_batching, no_automatic_morph_batching),
);
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<RenderMeshInstances>()
.init_resource::<MeshBindGroups>()
@ -160,9 +160,9 @@ impl Plugin for MeshRenderPlugin {
fn finish(&self, app: &mut App) {
let mut mesh_bindings_shader_defs = Vec::with_capacity(1);
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(per_object_buffer_batch_size) = GpuArrayBuffer::<MeshUniform>::batch_size(
render_app.world.resource::<RenderDevice>(),
render_app.world().resource::<RenderDevice>(),
) {
mesh_bindings_shader_defs.push(ShaderDefVal::UInt(
"PER_OBJECT_BUFFER_BATCH_SIZE".into(),
@ -172,7 +172,7 @@ impl Plugin for MeshRenderPlugin {
render_app
.insert_resource(GpuArrayBuffer::<MeshUniform>::new(
render_app.world.resource::<RenderDevice>(),
render_app.world().resource::<RenderDevice>(),
))
.init_resource::<MeshPipeline>();
}

View file

@ -72,12 +72,12 @@ impl Plugin for ScreenSpaceAmbientOcclusionPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
if !render_app
.world
.world()
.resource::<RenderAdapter>()
.get_texture_format_features(TextureFormat::R16Float)
.allowed_usages
@ -88,7 +88,7 @@ impl Plugin for ScreenSpaceAmbientOcclusionPlugin {
}
if render_app
.world
.world()
.resource::<RenderDevice>()
.limits()
.max_storage_textures_per_shader_stage

View file

@ -41,13 +41,13 @@ impl Plugin for CameraPlugin {
ExtractComponentPlugin::<CameraMainTextureUsages>::default(),
));
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<SortedCameras>()
.add_systems(ExtractSchedule, extract_cameras)
.add_systems(Render, sort_cameras.in_set(RenderSet::ManageViews));
let camera_driver_node = CameraDriverNode::new(&mut render_app.world);
let mut render_graph = render_app.world.resource_mut::<RenderGraph>();
let camera_driver_node = CameraDriverNode::new(render_app.world_mut());
let mut render_graph = render_app.world_mut().resource_mut::<RenderGraph>();
render_graph.add_node(crate::graph::CameraDriverLabel, camera_driver_node);
}
}

View file

@ -52,18 +52,18 @@ impl Plugin for RenderDiagnosticsPlugin {
app.insert_resource(render_diagnostics_mutex.clone())
.add_systems(PreUpdate, sync_diagnostics);
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.insert_resource(render_diagnostics_mutex);
}
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
let device = render_app.world.resource::<RenderDevice>();
let queue = render_app.world.resource::<RenderQueue>();
let device = render_app.world().resource::<RenderDevice>();
let queue = render_app.world().resource::<RenderQueue>();
render_app.insert_resource(DiagnosticsRecorder::new(device, queue));
}
}

View file

@ -80,7 +80,7 @@ impl<C> Default for UniformComponentPlugin<C> {
impl<C: Component + ShaderType + WriteInto + Clone> Plugin for UniformComponentPlugin<C> {
fn build(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.insert_resource(ComponentUniforms::<C>::default())
.add_systems(
@ -184,7 +184,7 @@ impl<C, F> ExtractComponentPlugin<C, F> {
impl<C: ExtractComponent> Plugin for ExtractComponentPlugin<C> {
fn build(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
if self.only_extract_visible {
render_app.add_systems(ExtractSchedule, extract_visible_components::<C>);
} else {

View file

@ -94,7 +94,7 @@ where
EI: ExtractInstance,
{
fn build(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.init_resource::<ExtractedInstances<EI>>();
if self.only_extract_visible {
render_app.add_systems(ExtractSchedule, extract_visible::<EI>);

View file

@ -31,7 +31,7 @@ impl<R: ExtractResource> Default for ExtractResourcePlugin<R> {
impl<R: ExtractResource> Plugin for ExtractResourcePlugin<R> {
fn build(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_systems(ExtractSchedule, extract_resource::<R>);
}
}

View file

@ -21,7 +21,7 @@ impl Plugin for GlobalsPlugin {
load_internal_asset!(app, GLOBALS_TYPE_HANDLE, "globals.wgsl", Shader::from_wgsl);
app.register_type::<GlobalsUniform>();
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<GlobalsBuffer>()
.init_resource::<Time>()

View file

@ -17,7 +17,7 @@ pub struct GpuComponentArrayBufferPlugin<C: Component + GpuArrayBufferable>(Phan
impl<C: Component + GpuArrayBufferable> Plugin for GpuComponentArrayBufferPlugin<C> {
fn build(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_systems(
Render,
prepare_gpu_component_array_buffers::<C>.in_set(RenderSet::PrepareResources),
@ -26,9 +26,9 @@ impl<C: Component + GpuArrayBufferable> Plugin for GpuComponentArrayBufferPlugin
}
fn finish(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.insert_resource(GpuArrayBuffer::<C>::new(
render_app.world.resource::<RenderDevice>(),
render_app.world().resource::<RenderDevice>(),
));
}
}

View file

@ -98,7 +98,7 @@ pub struct RenderPlugin {
pub synchronous_pipeline_compilation: bool,
}
/// The labels of the default App rendering sets.
/// The systems sets of the default [`App`] rendering schedule.
///
/// that runs immediately after the matching system set.
/// These can be useful for ordering, but you almost never want to add your systems to these sets.
@ -227,7 +227,7 @@ struct FutureRendererResources(
>,
);
/// A Label for the rendering sub-app.
/// A label for the rendering sub-app.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
pub struct RenderApp;
@ -267,8 +267,8 @@ impl Plugin for RenderPlugin {
let mut system_state: SystemState<
Query<&RawHandleWrapper, With<PrimaryWindow>>,
> = SystemState::new(&mut app.world);
let primary_window = system_state.get(&app.world).get_single().ok().cloned();
> = SystemState::new(app.world_mut());
let primary_window = system_state.get(app.world()).get_single().ok().cloned();
let settings = render_creation.clone();
let async_renderer = async move {
@ -347,7 +347,7 @@ impl Plugin for RenderPlugin {
}
fn ready(&self, app: &App) -> bool {
app.world
app.world()
.get_resource::<FutureRendererResources>()
.and_then(|frr| frr.0.try_lock().map(|locked| locked.is_some()).ok())
.unwrap_or(true)
@ -356,7 +356,7 @@ impl Plugin for RenderPlugin {
fn finish(&self, app: &mut App) {
load_internal_asset!(app, MATHS_SHADER_HANDLE, "maths.wgsl", Shader::from_wgsl);
if let Some(future_renderer_resources) =
app.world.remove_resource::<FutureRendererResources>()
app.world_mut().remove_resource::<FutureRendererResources>()
{
let (device, queue, adapter_info, render_adapter, instance) =
future_renderer_resources.0.lock().unwrap().take().unwrap();
@ -390,16 +390,15 @@ struct ScratchMainWorld(World);
/// Executes the [`ExtractSchedule`] step of the renderer.
/// This updates the render world with the extracted ECS data of the current frame.
fn extract(main_world: &mut World, render_app: &mut App) {
fn extract(main_world: &mut World, render_world: &mut World) {
// temporarily add the app world to the render world as a resource
let scratch_world = main_world.remove_resource::<ScratchMainWorld>().unwrap();
let inserted_world = std::mem::replace(main_world, scratch_world.0);
render_app.world.insert_resource(MainWorld(inserted_world));
render_app.world.run_schedule(ExtractSchedule);
render_world.insert_resource(MainWorld(inserted_world));
render_world.run_schedule(ExtractSchedule);
// move the app world back, as if nothing happened.
let inserted_world = render_app.world.remove_resource::<MainWorld>().unwrap();
let inserted_world = render_world.remove_resource::<MainWorld>().unwrap();
let scratch_world = std::mem::replace(main_world, inserted_world.0);
main_world.insert_resource(ScratchMainWorld(scratch_world));
}
@ -408,8 +407,8 @@ fn extract(main_world: &mut World, render_app: &mut App) {
unsafe fn initialize_render_app(app: &mut App) {
app.init_resource::<ScratchMainWorld>();
let mut render_app = App::empty();
render_app.main_schedule_label = Render.intern();
let mut render_app = SubApp::new();
render_app.update_schedule = Some(Render.intern());
let mut extract_schedule = Schedule::new(ExtractSchedule);
// We skip applying any commands during the ExtractSchedule
@ -424,7 +423,7 @@ unsafe fn initialize_render_app(app: &mut App) {
.add_schedule(extract_schedule)
.add_schedule(Render::base_schedule())
.init_resource::<render_graph::RenderGraph>()
.insert_resource(app.world.resource::<AssetServer>().clone())
.insert_resource(app.world().resource::<AssetServer>().clone())
.add_systems(ExtractSchedule, PipelineCache::extract_shaders)
.add_systems(
Render,
@ -441,11 +440,7 @@ unsafe fn initialize_render_app(app: &mut App) {
),
);
let (sender, receiver) = bevy_time::create_time_channels();
app.insert_resource(receiver);
render_app.insert_resource(sender);
app.insert_sub_app(RenderApp, SubApp::new(render_app, move |main_world, render_app| {
render_app.set_extract(|main_world, render_world| {
#[cfg(feature = "trace")]
let _render_span = bevy_utils::tracing::info_span!("extract main app to render subapp").entered();
{
@ -459,23 +454,27 @@ unsafe fn initialize_render_app(app: &mut App) {
let total_count = main_world.entities().total_count();
assert_eq!(
render_app.world.entities().len(),
render_world.entities().len(),
0,
"An entity was spawned after the entity list was cleared last frame and before the extract schedule began. This is not supported",
);
// SAFETY: This is safe given the clear_entities call in the past frame and the assert above
unsafe {
render_app
.world
render_world
.entities_mut()
.flush_and_reserve_invalid_assuming_no_entities(total_count);
}
}
// run extract schedule
extract(main_world, render_app);
}));
extract(main_world, render_world);
});
let (sender, receiver) = bevy_time::create_time_channels();
render_app.insert_resource(sender);
app.insert_resource(receiver);
app.insert_sub_app(RenderApp, render_app);
}
/// Applies the commands from the extract schedule. This happens during

View file

@ -29,7 +29,7 @@ impl Plugin for MeshPlugin {
// 'Mesh' must be prepared after 'Image' as meshes rely on the morph target image being ready
.add_plugins(RenderAssetPlugin::<Mesh, Image>::default());
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -1,6 +1,6 @@
use async_channel::{Receiver, Sender};
use bevy_app::{App, AppExit, AppLabel, Main, Plugin, SubApp};
use bevy_app::{App, AppExit, AppLabel, Plugin, SubApp};
use bevy_ecs::{
schedule::MainThreadExecutor,
system::Resource,
@ -106,20 +106,20 @@ pub struct PipelinedRenderingPlugin;
impl Plugin for PipelinedRenderingPlugin {
fn build(&self, app: &mut App) {
// Don't add RenderExtractApp if RenderApp isn't initialized.
if app.get_sub_app(RenderApp).is_err() {
if app.get_sub_app(RenderApp).is_none() {
return;
}
app.insert_resource(MainThreadExecutor::new());
let mut sub_app = App::empty();
sub_app.init_schedule(Main);
app.insert_sub_app(RenderExtractApp, SubApp::new(sub_app, update_rendering));
let mut sub_app = SubApp::new();
sub_app.set_extract(renderer_extract);
app.insert_sub_app(RenderExtractApp, sub_app);
}
// Sets up the render thread and inserts resources into the main app used for controlling the render thread.
fn cleanup(&self, app: &mut App) {
// skip setting up when headless
if app.get_sub_app(RenderExtractApp).is_err() {
if app.get_sub_app(RenderExtractApp).is_none() {
return;
}
@ -131,8 +131,8 @@ impl Plugin for PipelinedRenderingPlugin {
.expect("Unable to get RenderApp. Another plugin may have removed the RenderApp before PipelinedRenderingPlugin");
// clone main thread executor to render world
let executor = app.world.get_resource::<MainThreadExecutor>().unwrap();
render_app.app.world.insert_resource(executor.clone());
let executor = app.world().get_resource::<MainThreadExecutor>().unwrap();
render_app.world_mut().insert_resource(executor.clone());
render_to_app_sender.send_blocking(render_app).unwrap();
@ -161,7 +161,7 @@ impl Plugin for PipelinedRenderingPlugin {
#[cfg(feature = "trace")]
let _sub_app_span =
bevy_utils::tracing::info_span!("sub app", name = ?RenderApp).entered();
render_app.run();
render_app.update();
}
if render_to_app_sender.send_blocking(render_app).is_err() {
@ -176,7 +176,7 @@ impl Plugin for PipelinedRenderingPlugin {
// This function waits for the rendering world to be received,
// runs extract, and then sends the rendering world back to the render thread.
fn update_rendering(app_world: &mut World, _sub_app: &mut App) {
fn renderer_extract(app_world: &mut World, _world: &mut World) {
app_world.resource_scope(|world, main_thread_executor: Mut<MainThreadExecutor>| {
world.resource_scope(|world, mut render_channels: Mut<RenderAppChannels>| {
// we use a scope here to run any main thread tasks that the render world still needs to run

View file

@ -1,5 +1,5 @@
use crate::{ExtractSchedule, MainWorld, Render, RenderApp, RenderSet};
use bevy_app::{App, Plugin};
use bevy_app::{App, Plugin, SubApp};
use bevy_asset::{Asset, AssetEvent, AssetId, Assets};
use bevy_ecs::{
prelude::{Commands, EventReader, IntoSystemConfigs, ResMut, Resource},
@ -122,7 +122,7 @@ impl<A: RenderAsset, AFTER: RenderAssetDependency + 'static> Plugin
{
fn build(&self, app: &mut App) {
app.init_resource::<CachedExtractRenderAssetSystemState<A>>();
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<ExtractedAssets<A>>()
.init_resource::<RenderAssets<A>>()
@ -138,17 +138,17 @@ impl<A: RenderAsset, AFTER: RenderAssetDependency + 'static> Plugin
// helper to allow specifying dependencies between render assets
pub trait RenderAssetDependency {
fn register_system(render_app: &mut App, system: SystemConfigs);
fn register_system(render_app: &mut SubApp, system: SystemConfigs);
}
impl RenderAssetDependency for () {
fn register_system(render_app: &mut App, system: SystemConfigs) {
fn register_system(render_app: &mut SubApp, system: SystemConfigs) {
render_app.add_systems(Render, system);
}
}
impl<A: RenderAsset> RenderAssetDependency for A {
fn register_system(render_app: &mut App, system: SystemConfigs) {
fn register_system(render_app: &mut SubApp, system: SystemConfigs) {
render_app.add_systems(Render, system.after(prepare_assets::<A>));
}
}

View file

@ -1,10 +1,10 @@
use bevy_app::App;
use bevy_app::{App, SubApp};
use bevy_ecs::world::FromWorld;
use bevy_utils::tracing::warn;
use super::{IntoRenderNodeArray, Node, RenderGraph, RenderLabel, RenderSubGraph};
/// Adds common [`RenderGraph`] operations to [`App`].
/// Adds common [`RenderGraph`] operations to [`SubApp`] (and [`App`]).
pub trait RenderGraphApp {
// Add a sub graph to the [`RenderGraph`]
fn add_render_sub_graph(&mut self, sub_graph: impl RenderSubGraph) -> &mut Self;
@ -32,23 +32,15 @@ pub trait RenderGraphApp {
) -> &mut Self;
}
impl RenderGraphApp for App {
fn add_render_sub_graph(&mut self, sub_graph: impl RenderSubGraph) -> &mut Self {
let mut render_graph = self.world.get_resource_mut::<RenderGraph>().expect(
"RenderGraph not found. Make sure you are using add_render_sub_graph on the RenderApp",
);
render_graph.add_sub_graph(sub_graph, RenderGraph::default());
self
}
impl RenderGraphApp for SubApp {
fn add_render_graph_node<T: Node + FromWorld>(
&mut self,
sub_graph: impl RenderSubGraph,
node_label: impl RenderLabel,
) -> &mut Self {
let sub_graph = sub_graph.intern();
let node = T::from_world(&mut self.world);
let mut render_graph = self.world.get_resource_mut::<RenderGraph>().expect(
let node = T::from_world(self.world_mut());
let mut render_graph = self.world_mut().get_resource_mut::<RenderGraph>().expect(
"RenderGraph not found. Make sure you are using add_render_graph_node on the RenderApp",
);
if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph) {
@ -67,7 +59,7 @@ impl RenderGraphApp for App {
edges: impl IntoRenderNodeArray<N>,
) -> &mut Self {
let sub_graph = sub_graph.intern();
let mut render_graph = self.world.get_resource_mut::<RenderGraph>().expect(
let mut render_graph = self.world_mut().get_resource_mut::<RenderGraph>().expect(
"RenderGraph not found. Make sure you are using add_render_graph_edges on the RenderApp",
);
if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph) {
@ -87,7 +79,7 @@ impl RenderGraphApp for App {
input_node: impl RenderLabel,
) -> &mut Self {
let sub_graph = sub_graph.intern();
let mut render_graph = self.world.get_resource_mut::<RenderGraph>().expect(
let mut render_graph = self.world_mut().get_resource_mut::<RenderGraph>().expect(
"RenderGraph not found. Make sure you are using add_render_graph_edge on the RenderApp",
);
if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph) {
@ -99,4 +91,47 @@ impl RenderGraphApp for App {
}
self
}
fn add_render_sub_graph(&mut self, sub_graph: impl RenderSubGraph) -> &mut Self {
let mut render_graph = self.world_mut().get_resource_mut::<RenderGraph>().expect(
"RenderGraph not found. Make sure you are using add_render_sub_graph on the RenderApp",
);
render_graph.add_sub_graph(sub_graph, RenderGraph::default());
self
}
}
impl RenderGraphApp for App {
fn add_render_graph_node<T: Node + FromWorld>(
&mut self,
sub_graph: impl RenderSubGraph,
node_label: impl RenderLabel,
) -> &mut Self {
SubApp::add_render_graph_node::<T>(self.main_mut(), sub_graph, node_label);
self
}
fn add_render_graph_edge(
&mut self,
sub_graph: impl RenderSubGraph,
output_node: impl RenderLabel,
input_node: impl RenderLabel,
) -> &mut Self {
SubApp::add_render_graph_edge(self.main_mut(), sub_graph, output_node, input_node);
self
}
fn add_render_graph_edges<const N: usize>(
&mut self,
sub_graph: impl RenderSubGraph,
edges: impl IntoRenderNodeArray<N>,
) -> &mut Self {
SubApp::add_render_graph_edges(self.main_mut(), sub_graph, edges);
self
}
fn add_render_sub_graph(&mut self, sub_graph: impl RenderSubGraph) -> &mut Self {
SubApp::add_render_sub_graph(self.main_mut(), sub_graph);
self
}
}

View file

@ -1,5 +1,5 @@
use crate::render_phase::{PhaseItem, TrackedRenderPass};
use bevy_app::App;
use bevy_app::{App, SubApp};
use bevy_ecs::{
entity::Entity,
query::{QueryState, ROQueryItem, ReadOnlyQueryData},
@ -310,16 +310,16 @@ pub trait AddRenderCommand {
C::Param: ReadOnlySystemParam;
}
impl AddRenderCommand for App {
impl AddRenderCommand for SubApp {
fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
&mut self,
) -> &mut Self
where
C::Param: ReadOnlySystemParam,
{
let draw_function = RenderCommandState::<P, C>::new(&mut self.world);
let draw_function = RenderCommandState::<P, C>::new(self.world_mut());
let draw_functions = self
.world
.world()
.get_resource::<DrawFunctions<P>>()
.unwrap_or_else(|| {
panic!(
@ -332,3 +332,15 @@ impl AddRenderCommand for App {
self
}
}
impl AddRenderCommand for App {
fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
&mut self,
) -> &mut Self
where
C::Param: ReadOnlySystemParam,
{
SubApp::add_render_command::<P, C>(self.main_mut());
self
}
}

View file

@ -88,12 +88,13 @@ impl Plugin for ImagePlugin {
.register_type::<Image>()
.init_asset::<Image>()
.register_asset_reflect::<Image>();
app.world
app.world_mut()
.resource_mut::<Assets<Image>>()
.insert(&Handle::default(), Image::default());
#[cfg(feature = "basis-universal")]
if let Some(processor) = app
.world
.world()
.get_resource::<bevy_asset::processor::AssetProcessor>()
{
processor.register_processor::<bevy_asset::processor::LoadAndSave<ImageLoader, CompressedImageSaver>>(
@ -103,7 +104,7 @@ impl Plugin for ImagePlugin {
.set_default_processor::<bevy_asset::processor::LoadAndSave<ImageLoader, CompressedImageSaver>>("png");
}
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.init_resource::<TextureCache>().add_systems(
Render,
update_texture_cache_system.in_set(RenderSet::Cleanup),
@ -140,9 +141,9 @@ impl Plugin for ImagePlugin {
app.init_asset_loader::<ImageLoader>();
}
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
let default_sampler = {
let device = render_app.world.resource::<RenderDevice>();
let device = render_app.world().resource::<RenderDevice>();
device.create_sampler(&self.default_sampler.as_wgpu())
};
render_app

View file

@ -55,7 +55,7 @@ impl Plugin for ViewPlugin {
// NOTE: windows.is_changed() handles cases where a window was resized
.add_plugins((ExtractResourcePlugin::<Msaa>::default(), VisibilityPlugin));
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.init_resource::<ViewUniforms>().add_systems(
Render,
(

View file

@ -475,42 +475,51 @@ mod test {
let mut app = App::new();
app.add_systems(Update, visibility_propagate_system);
let root1 = app.world.spawn(visibility_bundle(Visibility::Hidden)).id();
let root1_child1 = app.world.spawn(VisibilityBundle::default()).id();
let root1_child2 = app.world.spawn(visibility_bundle(Visibility::Hidden)).id();
let root1_child1_grandchild1 = app.world.spawn(VisibilityBundle::default()).id();
let root1_child2_grandchild1 = app.world.spawn(VisibilityBundle::default()).id();
let root1 = app
.world_mut()
.spawn(visibility_bundle(Visibility::Hidden))
.id();
let root1_child1 = app.world_mut().spawn(VisibilityBundle::default()).id();
let root1_child2 = app
.world_mut()
.spawn(visibility_bundle(Visibility::Hidden))
.id();
let root1_child1_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id();
let root1_child2_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id();
app.world
app.world_mut()
.entity_mut(root1)
.push_children(&[root1_child1, root1_child2]);
app.world
app.world_mut()
.entity_mut(root1_child1)
.push_children(&[root1_child1_grandchild1]);
app.world
app.world_mut()
.entity_mut(root1_child2)
.push_children(&[root1_child2_grandchild1]);
let root2 = app.world.spawn(VisibilityBundle::default()).id();
let root2_child1 = app.world.spawn(VisibilityBundle::default()).id();
let root2_child2 = app.world.spawn(visibility_bundle(Visibility::Hidden)).id();
let root2_child1_grandchild1 = app.world.spawn(VisibilityBundle::default()).id();
let root2_child2_grandchild1 = app.world.spawn(VisibilityBundle::default()).id();
let root2 = app.world_mut().spawn(VisibilityBundle::default()).id();
let root2_child1 = app.world_mut().spawn(VisibilityBundle::default()).id();
let root2_child2 = app
.world_mut()
.spawn(visibility_bundle(Visibility::Hidden))
.id();
let root2_child1_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id();
let root2_child2_grandchild1 = app.world_mut().spawn(VisibilityBundle::default()).id();
app.world
app.world_mut()
.entity_mut(root2)
.push_children(&[root2_child1, root2_child2]);
app.world
app.world_mut()
.entity_mut(root2_child1)
.push_children(&[root2_child1_grandchild1]);
app.world
app.world_mut()
.entity_mut(root2_child2)
.push_children(&[root2_child2_grandchild1]);
app.update();
let is_visible = |e: Entity| {
app.world
app.world()
.entity(e)
.get::<InheritedVisibility>()
.unwrap()
@ -566,29 +575,29 @@ mod test {
let mut app = App::new();
app.add_systems(Update, visibility_propagate_system);
let root1 = app.world.spawn(visibility_bundle(Visible)).id();
let root1_child1 = app.world.spawn(visibility_bundle(Inherited)).id();
let root1_child2 = app.world.spawn(visibility_bundle(Hidden)).id();
let root1_child1_grandchild1 = app.world.spawn(visibility_bundle(Visible)).id();
let root1_child2_grandchild1 = app.world.spawn(visibility_bundle(Visible)).id();
let root1 = app.world_mut().spawn(visibility_bundle(Visible)).id();
let root1_child1 = app.world_mut().spawn(visibility_bundle(Inherited)).id();
let root1_child2 = app.world_mut().spawn(visibility_bundle(Hidden)).id();
let root1_child1_grandchild1 = app.world_mut().spawn(visibility_bundle(Visible)).id();
let root1_child2_grandchild1 = app.world_mut().spawn(visibility_bundle(Visible)).id();
let root2 = app.world.spawn(visibility_bundle(Inherited)).id();
let root3 = app.world.spawn(visibility_bundle(Hidden)).id();
let root2 = app.world_mut().spawn(visibility_bundle(Inherited)).id();
let root3 = app.world_mut().spawn(visibility_bundle(Hidden)).id();
app.world
app.world_mut()
.entity_mut(root1)
.push_children(&[root1_child1, root1_child2]);
app.world
app.world_mut()
.entity_mut(root1_child1)
.push_children(&[root1_child1_grandchild1]);
app.world
app.world_mut()
.entity_mut(root1_child2)
.push_children(&[root1_child2_grandchild1]);
app.update();
let is_visible = |e: Entity| {
app.world
app.world()
.entity(e)
.get::<InheritedVisibility>()
.unwrap()

View file

@ -37,7 +37,7 @@ impl Plugin for WindowRenderPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(ScreenshotPlugin);
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<ExtractedWindows>()
.init_resource::<WindowSurfaces>()
@ -53,7 +53,7 @@ impl Plugin for WindowRenderPlugin {
}
fn finish(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.init_resource::<ScreenshotToScreenPipeline>();
}
}

View file

@ -137,7 +137,7 @@ impl Plugin for ScreenshotPlugin {
}
fn finish(&self, app: &mut bevy_app::App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.init_resource::<SpecializedRenderPipelines<ScreenshotToScreenPipeline>>();
}
}

View file

@ -132,15 +132,18 @@ mod tests {
let mut scene_world = World::new();
// create a new DynamicScene manually
let type_registry = app.world.resource::<AppTypeRegistry>().clone();
let type_registry = app.world().resource::<AppTypeRegistry>().clone();
scene_world.insert_resource(type_registry);
scene_world.spawn(ComponentA { x: 3.0, y: 4.0 });
let scene = DynamicScene::from_world(&scene_world);
let scene_handle = app.world.resource_mut::<Assets<DynamicScene>>().add(scene);
let scene_handle = app
.world_mut()
.resource_mut::<Assets<DynamicScene>>()
.add(scene);
// spawn the scene as a child of `entity` using the `DynamicSceneBundle`
let entity = app
.world
.world_mut()
.spawn(DynamicSceneBundle {
scene: scene_handle.clone(),
..default()
@ -152,24 +155,27 @@ mod tests {
// make sure that the scene was added as a child of the root entity
let (scene_entity, scene_component_a) = app
.world
.world_mut()
.query::<(Entity, &ComponentA)>()
.single(&app.world);
.single(app.world());
assert_eq!(scene_component_a.x, 3.0);
assert_eq!(scene_component_a.y, 4.0);
assert_eq!(app.world.entity(entity).get::<Children>().unwrap().len(), 1);
assert_eq!(
app.world().entity(entity).get::<Children>().unwrap().len(),
1
);
// let's try to delete the scene
let mut scene_spawner = app.world.resource_mut::<SceneSpawner>();
let mut scene_spawner = app.world_mut().resource_mut::<SceneSpawner>();
scene_spawner.despawn(&scene_handle);
// run the scene spawner system to despawn the scene
app.update();
// the scene entity does not exist anymore
assert!(app.world.get_entity(scene_entity).is_none());
assert!(app.world().get_entity(scene_entity).is_none());
// the root entity does not have any children anymore
assert!(app.world.entity(entity).get::<Children>().is_none());
assert!(app.world().entity(entity).get::<Children>().is_none());
}
}

View file

@ -61,7 +61,7 @@ impl Plugin for ScenePlugin {
.add_systems(SpawnScene, (scene_spawner, scene_spawner_system).chain());
// Register component hooks for DynamicScene
app.world
app.world_mut()
.register_component_hooks::<Handle<DynamicScene>>()
.on_remove(|mut world, entity, _| {
let Some(handle) = world.get::<Handle<DynamicScene>>(entity) else {
@ -80,7 +80,7 @@ impl Plugin for ScenePlugin {
});
// Register component hooks for Scene
app.world
app.world_mut()
.register_component_hooks::<Handle<Scene>>()
.on_remove(|mut world, entity, _| {
if let Some(&SceneInstance(scene_instance)) = world.get::<SceneInstance>(entity) {

View file

@ -512,18 +512,18 @@ mod tests {
app.add_plugins((AssetPlugin::default(), ScenePlugin));
app.register_type::<ComponentA>();
app.world.spawn(ComponentA);
app.world.spawn(ComponentA);
app.world_mut().spawn(ComponentA);
app.world_mut().spawn(ComponentA);
// Build scene.
let scene =
app.world
app.world_mut()
.run_system_once(|world: &World, asset_server: Res<'_, AssetServer>| {
asset_server.add(DynamicScene::from_world(world))
});
// Spawn scene.
let scene_entity = app.world.run_system_once(
let scene_entity = app.world_mut().run_system_once(
move |mut commands: Commands<'_, '_>, mut scene_spawner: ResMut<'_, SceneSpawner>| {
let scene_entity = commands.spawn_empty().id();
scene_spawner.spawn_dynamic_as_child(scene.clone(), scene_entity);
@ -533,7 +533,7 @@ mod tests {
// Check for event arrival.
app.update();
app.world.run_system_once(
app.world_mut().run_system_once(
move |mut ev_scene: EventReader<'_, '_, SceneInstanceReady>| {
let mut events = ev_scene.read();
@ -555,7 +555,7 @@ mod tests {
app.add_plugins((AssetPlugin::default(), ScenePlugin));
app.register_type::<ComponentA>();
let asset_server = app.world.resource::<AssetServer>();
let asset_server = app.world().resource::<AssetServer>();
// Build scene.
let scene = asset_server.add(DynamicScene::default());
@ -573,14 +573,14 @@ mod tests {
// Spawn scene.
for _ in 0..count {
app.world.spawn((ComponentA, scene.clone()));
app.world_mut().spawn((ComponentA, scene.clone()));
}
app.update();
check(&mut app.world, count);
check(app.world_mut(), count);
// Despawn scene.
app.world.run_system_once(
app.world_mut().run_system_once(
|mut commands: Commands, query: Query<Entity, With<ComponentA>>| {
for entity in query.iter() {
commands.entity(entity).despawn_recursive();
@ -589,6 +589,6 @@ mod tests {
);
app.update();
check(&mut app.world, 0);
check(app.world_mut(), 0);
}
}

View file

@ -97,7 +97,7 @@ impl Plugin for SpritePlugin {
),
);
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<ImageBindGroups>()
.init_resource::<SpecializedRenderPipelines<SpritePipeline>>()
@ -125,7 +125,7 @@ impl Plugin for SpritePlugin {
}
fn finish(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.init_resource::<SpritePipeline>();
}
}
@ -202,11 +202,14 @@ mod test {
app.add_systems(Update, calculate_bounds_2d);
// Add entities
let entity = app.world.spawn((Sprite::default(), image_handle)).id();
let entity = app
.world_mut()
.spawn((Sprite::default(), image_handle))
.id();
// Verify that the entity does not have an AABB
assert!(!app
.world
.world()
.get_entity(entity)
.expect("Could not find entity")
.contains::<Aabb>());
@ -216,7 +219,7 @@ mod test {
// Verify the AABB exists
assert!(app
.world
.world()
.get_entity(entity)
.expect("Could not find entity")
.contains::<Aabb>());
@ -241,7 +244,7 @@ mod test {
// Add entities
let entity = app
.world
.world_mut()
.spawn((
Sprite {
custom_size: Some(Vec2::ZERO),
@ -256,7 +259,7 @@ mod test {
// Get the initial AABB
let first_aabb = *app
.world
.world()
.get_entity(entity)
.expect("Could not find entity")
.get::<Aabb>()
@ -264,7 +267,7 @@ mod test {
// Change `custom_size` of sprite
let mut binding = app
.world
.world_mut()
.get_entity_mut(entity)
.expect("Could not find entity");
let mut sprite = binding
@ -277,7 +280,7 @@ mod test {
// Get the re-calculated AABB
let second_aabb = *app
.world
.world()
.get_entity(entity)
.expect("Could not find entity")
.get::<Aabb>()

View file

@ -24,13 +24,15 @@ impl Plugin for ColorMaterialPlugin {
app.add_plugins(Material2dPlugin::<ColorMaterial>::default())
.register_asset_reflect::<ColorMaterial>();
app.world.resource_mut::<Assets<ColorMaterial>>().insert(
&Handle::<ColorMaterial>::default(),
ColorMaterial {
color: Color::srgb(1.0, 0.0, 1.0),
..Default::default()
},
);
app.world_mut()
.resource_mut::<Assets<ColorMaterial>>()
.insert(
&Handle::<ColorMaterial>::default(),
ColorMaterial {
color: Color::srgb(1.0, 0.0, 1.0),
..Default::default()
},
);
}
}

View file

@ -150,7 +150,7 @@ where
fn build(&self, app: &mut App) {
app.init_asset::<M>();
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.add_render_command::<Transparent2d, DrawMaterial2d<M>>()
.init_resource::<ExtractedMaterials2d<M>>()
@ -176,7 +176,7 @@ where
}
fn finish(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.init_resource::<Material2dPipeline<M>>();
}
}

View file

@ -93,7 +93,7 @@ impl Plugin for Mesh2dRenderPlugin {
);
load_internal_asset!(app, MESH2D_SHADER_HANDLE, "mesh2d.wgsl", Shader::from_wgsl);
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.init_resource::<RenderMesh2dInstances>()
.init_resource::<SpecializedMeshPipelines<Mesh2dPipeline>>()
@ -115,9 +115,9 @@ impl Plugin for Mesh2dRenderPlugin {
fn finish(&self, app: &mut bevy_app::App) {
let mut mesh_bindings_shader_defs = Vec::with_capacity(1);
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(per_object_buffer_batch_size) = GpuArrayBuffer::<Mesh2dUniform>::batch_size(
render_app.world.resource::<RenderDevice>(),
render_app.world().resource::<RenderDevice>(),
) {
mesh_bindings_shader_defs.push(ShaderDefVal::UInt(
"PER_OBJECT_BUFFER_BATCH_SIZE".into(),
@ -127,7 +127,7 @@ impl Plugin for Mesh2dRenderPlugin {
render_app
.insert_resource(GpuArrayBuffer::<Mesh2dUniform>::new(
render_app.world.resource::<RenderDevice>(),
render_app.world().resource::<RenderDevice>(),
))
.init_resource::<Mesh2dPipeline>();
}

View file

@ -104,7 +104,7 @@ impl Plugin for TextPlugin {
),
);
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_systems(
ExtractSchedule,
extract_text2d_sprite.after(SpriteSystem::ExtractSprites),

View file

@ -301,7 +301,7 @@ mod tests {
);
let entity = app
.world
.world_mut()
.spawn((Text2dBundle {
text: Text::from_section(FIRST_TEXT, default()),
..default()
@ -316,7 +316,7 @@ mod tests {
let (mut app, entity) = setup();
assert!(!app
.world
.world()
.get_entity(entity)
.expect("Could not find entity")
.contains::<Aabb>());
@ -325,7 +325,7 @@ mod tests {
app.update();
let aabb = app
.world
.world()
.get_entity(entity)
.expect("Could not find entity")
.get::<Aabb>()
@ -347,14 +347,14 @@ mod tests {
app.update();
let first_aabb = *app
.world
.world()
.get_entity(entity)
.expect("Could not find entity")
.get::<Aabb>()
.expect("Could not find initial AABB");
let mut entity_ref = app
.world
.world_mut()
.get_entity_mut(entity)
.expect("Could not find entity");
*entity_ref
@ -365,7 +365,7 @@ mod tests {
app.update();
let second_aabb = *app
.world
.world()
.get_entity(entity)
.expect("Could not find entity")
.get::<Aabb>()

View file

@ -121,7 +121,7 @@ mod tests {
let mut entity = None;
for transform in transforms {
let mut e = app.world.spawn(TransformBundle::from(transform));
let mut e = app.world_mut().spawn(TransformBundle::from(transform));
if let Some(entity) = entity {
e.set_parent(entity);
@ -134,10 +134,10 @@ mod tests {
app.update();
let transform = *app.world.get::<GlobalTransform>(leaf_entity).unwrap();
let transform = *app.world().get::<GlobalTransform>(leaf_entity).unwrap();
let mut state = SystemState::<TransformHelper>::new(&mut app.world);
let helper = state.get(&app.world);
let mut state = SystemState::<TransformHelper>::new(app.world_mut());
let helper = state.get(app.world());
let computed_transform = helper.compute_global_transform(leaf_entity).unwrap();

View file

@ -416,7 +416,7 @@ mod test {
let mut child = Entity::from_raw(0);
let mut grandchild = Entity::from_raw(1);
let parent = app
.world
.world_mut()
.spawn((
Transform::from_translation(translation),
GlobalTransform::IDENTITY,
@ -434,13 +434,16 @@ mod test {
app.update();
// check the `Children` structure is spawned
assert_eq!(&**app.world.get::<Children>(parent).unwrap(), &[child]);
assert_eq!(&**app.world.get::<Children>(child).unwrap(), &[grandchild]);
assert_eq!(&**app.world().get::<Children>(parent).unwrap(), &[child]);
assert_eq!(
&**app.world().get::<Children>(child).unwrap(),
&[grandchild]
);
// Note that at this point, the `GlobalTransform`s will not have updated yet, due to `Commands` delay
app.update();
let mut state = app.world.query::<&GlobalTransform>();
for global in state.iter(&app.world) {
let mut state = app.world_mut().query::<&GlobalTransform>();
for global in state.iter(app.world()) {
assert_eq!(global, &GlobalTransform::from_translation(translation));
}
}
@ -468,16 +471,16 @@ mod test {
}
let (temp_child, temp_grandchild) = setup_world(&mut temp);
let (child, grandchild) = setup_world(&mut app.world);
let (child, grandchild) = setup_world(app.world_mut());
assert_eq!(temp_child, child);
assert_eq!(temp_grandchild, grandchild);
app.world
app.world_mut()
.spawn(TransformBundle::IDENTITY)
.push_children(&[child]);
std::mem::swap(
&mut *app.world.get_mut::<Parent>(child).unwrap(),
&mut *app.world_mut().get_mut::<Parent>(child).unwrap(),
&mut *temp.get_mut::<Parent>(grandchild).unwrap(),
);

View file

@ -173,7 +173,7 @@ impl Plugin for UiPlugin {
}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -69,7 +69,7 @@ pub enum RenderUiSystem {
pub fn build_ui_render(app: &mut App) {
load_internal_asset!(app, UI_SHADER_HANDLE, "ui.wgsl", Shader::from_wgsl);
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
@ -116,7 +116,7 @@ pub fn build_ui_render(app: &mut App) {
// Render graph
let ui_graph_2d = get_ui_graph(render_app);
let ui_graph_3d = get_ui_graph(render_app);
let mut graph = render_app.world.resource_mut::<RenderGraph>();
let mut graph = render_app.world_mut().resource_mut::<RenderGraph>();
if let Some(graph_2d) = graph.get_sub_graph_mut(Core2d) {
graph_2d.add_sub_graph(SubGraphUi, ui_graph_2d);
@ -135,8 +135,8 @@ pub fn build_ui_render(app: &mut App) {
}
}
fn get_ui_graph(render_app: &mut App) -> RenderGraph {
let ui_pass_node = UiPassNode::new(&mut render_app.world);
fn get_ui_graph(render_app: &mut SubApp) -> RenderGraph {
let ui_pass_node = UiPassNode::new(render_app.world_mut());
let mut ui_graph = RenderGraph::default();
ui_graph.add_node(NodeUi::UiPass, ui_pass_node);
ui_graph

View file

@ -62,7 +62,7 @@ where
app.init_asset::<M>()
.add_plugins(ExtractComponentPlugin::<Handle<M>>::extract_visible());
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.add_render_command::<TransparentUi, DrawUiMaterial<M>>()
.init_resource::<ExtractedUiMaterials<M>>()
@ -89,7 +89,7 @@ where
}
fn finish(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.init_resource::<UiMaterialPipeline<M>>();
}
}

View file

@ -107,11 +107,11 @@ impl Plugin for WindowPlugin {
if let Some(primary_window) = &self.primary_window {
let initial_focus = app
.world
.world_mut()
.spawn(primary_window.clone())
.insert(PrimaryWindow)
.id();
if let Some(mut focus) = app.world.get_resource_mut::<Focus>() {
if let Some(mut focus) = app.world_mut().get_resource_mut::<Focus>() {
**focus = Some(initial_focus);
}
}

View file

@ -158,9 +158,9 @@ impl Plugin for WinitPlugin {
// Otherwise, we want to create a window before `bevy_render` initializes the renderer
// so that we have a surface to use as a hint. This improves compatibility with `wgpu`
// backends, especially WASM/WebGL2.
let mut create_window = SystemState::<CreateWindowParams>::from_world(&mut app.world);
create_windows(&event_loop, create_window.get_mut(&mut app.world));
create_window.apply(&mut app.world);
let mut create_window = SystemState::<CreateWindowParams>::from_world(app.world_mut());
create_windows(&event_loop, create_window.get_mut(app.world_mut()));
create_window.apply(app.world_mut());
}
// `winit`'s windows are bound to the event loop that created them, so the event loop must
@ -270,11 +270,11 @@ pub fn winit_runner(mut app: App) {
}
let event_loop = app
.world
.world_mut()
.remove_non_send_resource::<EventLoop<UserEvent>>()
.unwrap();
app.world
app.world_mut()
.insert_non_send_resource(event_loop.create_proxy());
let mut runner_state = WinitAppRunnerState::default();
@ -284,17 +284,17 @@ pub fn winit_runner(mut app: App) {
let mut redraw_event_reader = ManualEventReader::<RequestRedraw>::default();
let mut focused_windows_state: SystemState<(Res<WinitSettings>, Query<&Window>)> =
SystemState::new(&mut app.world);
SystemState::new(app.world_mut());
let mut event_writer_system_state: SystemState<(
EventWriter<WindowResized>,
NonSend<WinitWindows>,
Query<(&mut Window, &mut CachedWindow)>,
NonSend<AccessKitAdapters>,
)> = SystemState::new(&mut app.world);
)> = SystemState::new(app.world_mut());
let mut create_window =
SystemState::<CreateWindowParams<Added<Window>>>::from_world(&mut app.world);
SystemState::<CreateWindowParams<Added<Window>>>::from_world(app.world_mut());
let mut winit_events = Vec::default();
// set up the event loop
let event_handler = move |event, event_loop: &EventLoopWindowTarget<UserEvent>| {
@ -350,7 +350,7 @@ fn handle_winit_event(
}
runner_state.redraw_requested = true;
if let Some(app_exit_events) = app.world.get_resource::<Events<AppExit>>() {
if let Some(app_exit_events) = app.world().get_resource::<Events<AppExit>>() {
if app_exit_event_reader.read(app_exit_events).last().is_some() {
event_loop.exit();
return;
@ -360,7 +360,7 @@ fn handle_winit_event(
match event {
Event::AboutToWait => {
let (config, windows) = focused_windows_state.get(&app.world);
let (config, windows) = focused_windows_state.get(app.world());
let focused = windows.iter().any(|window| window.focused);
let mut should_update = match config.update_mode(focused) {
UpdateMode::Continuous => {
@ -394,7 +394,7 @@ fn handle_winit_event(
if should_update {
let visible = windows.iter().any(|window| window.visible);
let (_, winit_windows, _, _) = event_writer_system_state.get_mut(&mut app.world);
let (_, winit_windows, _, _) = event_writer_system_state.get_mut(app.world_mut());
if visible && runner_state.active != ActiveState::WillSuspend {
for window in winit_windows.windows.values() {
window.request_redraw();
@ -431,7 +431,7 @@ fn handle_winit_event(
event, window_id, ..
} => {
let (mut window_resized, winit_windows, mut windows, access_kit_adapters) =
event_writer_system_state.get_mut(&mut app.world);
event_writer_system_state.get_mut(app.world_mut());
let Some(window) = winit_windows.get_window_entity(window_id) else {
warn!("Skipped event {event:?} for unknown winit Window Id {window_id:?}");
@ -643,8 +643,8 @@ fn handle_winit_event(
_ => {}
}
let mut windows = app.world.query::<(&mut Window, &mut CachedWindow)>();
if let Ok((window_component, mut cache)) = windows.get_mut(&mut app.world, window) {
let mut windows = app.world_mut().query::<(&mut Window, &mut CachedWindow)>();
if let Ok((window_component, mut cache)) = windows.get_mut(app.world_mut(), window) {
if window_component.is_changed() {
cache.window = window_component.clone();
}
@ -667,8 +667,8 @@ fn handle_winit_event(
#[cfg(any(target_os = "android", target_os = "ios", target_os = "macos"))]
{
if runner_state.active == ActiveState::NotYetStarted {
create_windows(event_loop, create_window.get_mut(&mut app.world));
create_window.apply(&mut app.world);
create_windows(event_loop, create_window.get_mut(app.world_mut()));
create_window.apply(app.world_mut());
}
}
@ -683,9 +683,9 @@ fn handle_winit_event(
// Get windows that are cached but without raw handles. Those window were already created, but got their
// handle wrapper removed when the app was suspended.
let mut query = app
.world
.world_mut()
.query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<bevy_window::RawHandleWrapper>)>();
if let Ok((entity, window)) = query.get_single(&app.world) {
if let Ok((entity, window)) = query.get_single(app.world()) {
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
let window = window.clone();
@ -695,7 +695,7 @@ fn handle_winit_event(
mut adapters,
mut handlers,
accessibility_requested,
) = create_window.get_mut(&mut app.world);
) = create_window.get_mut(app.world_mut());
let winit_window = winit_windows.create_window(
event_loop,
@ -711,7 +711,7 @@ fn handle_winit_event(
display_handle: winit_window.display_handle().unwrap().as_raw(),
};
app.world.entity_mut(entity).insert(wrapper);
app.world_mut().entity_mut(entity).insert(wrapper);
}
event_loop.set_control_flow(ControlFlow::Wait);
}
@ -753,9 +753,13 @@ fn run_app_update_if_should(
{
// Remove the `RawHandleWrapper` from the primary window.
// This will trigger the surface destruction.
let mut query = app.world.query_filtered::<Entity, With<PrimaryWindow>>();
let entity = query.single(&app.world);
app.world.entity_mut(entity).remove::<RawHandleWrapper>();
let mut query = app
.world_mut()
.query_filtered::<Entity, With<PrimaryWindow>>();
let entity = query.single(app.world());
app.world_mut()
.entity_mut(entity)
.remove::<RawHandleWrapper>();
event_loop.set_control_flow(ControlFlow::Wait);
}
}
@ -766,7 +770,7 @@ fn run_app_update_if_should(
app.update();
// decide when to run the next update
let (config, windows) = focused_windows_state.get(&app.world);
let (config, windows) = focused_windows_state.get(app.world());
let focused = windows.iter().any(|window| window.focused);
match config.update_mode(focused) {
UpdateMode::Continuous => {
@ -787,13 +791,13 @@ fn run_app_update_if_should(
}
}
if let Some(app_redraw_events) = app.world.get_resource::<Events<RequestRedraw>>() {
if let Some(app_redraw_events) = app.world().get_resource::<Events<RequestRedraw>>() {
if redraw_event_reader.read(app_redraw_events).last().is_some() {
runner_state.redraw_requested = true;
}
}
if let Some(app_exit_events) = app.world.get_resource::<Events<AppExit>>() {
if let Some(app_exit_events) = app.world().get_resource::<Events<AppExit>>() {
if app_exit_event_reader.read(app_exit_events).last().is_some() {
event_loop.exit();
}
@ -802,8 +806,8 @@ fn run_app_update_if_should(
// create any new windows
// (even if app did not update, some may have been created by plugin setup)
create_windows(event_loop, create_window.get_mut(&mut app.world));
create_window.apply(&mut app.world);
create_windows(event_loop, create_window.get_mut(app.world_mut()));
create_window.apply(app.world_mut());
}
fn react_to_resize(

View file

@ -197,83 +197,83 @@ pub(crate) fn forward_winit_events(buffered_events: &mut Vec<WinitEvent>, app: &
for winit_event in buffered_events.iter() {
match winit_event.clone() {
WinitEvent::ApplicationLifetime(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::CursorEntered(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::CursorLeft(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::CursorMoved(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::FileDragAndDrop(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::Ime(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::ReceivedCharacter(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::RequestRedraw(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::WindowBackendScaleFactorChanged(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::WindowCloseRequested(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::WindowCreated(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::WindowDestroyed(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::WindowFocused(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::WindowMoved(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::WindowOccluded(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::WindowResized(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::WindowScaleFactorChanged(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::WindowThemeChanged(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::MouseButtonInput(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::MouseMotion(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::MouseWheel(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::TouchpadMagnify(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::TouchpadRotate(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::TouchInput(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
WinitEvent::KeyboardInput(e) => {
app.world.send_event(e);
app.world_mut().send_event(e);
}
}
}
app.world
app.world_mut()
.resource_mut::<Events<WinitEvent>>()
.send_batch(buffered_events.drain(..));
}

View file

@ -285,7 +285,7 @@ pub const COLORED_MESH2D_SHADER_HANDLE: Handle<Shader> =
impl Plugin for ColoredMesh2dPlugin {
fn build(&self, app: &mut App) {
// Load our custom shader
let mut shaders = app.world.resource_mut::<Assets<Shader>>();
let mut shaders = app.world_mut().resource_mut::<Assets<Shader>>();
shaders.insert(
&COLORED_MESH2D_SHADER_HANDLE,
Shader::from_wgsl(COLORED_MESH2D_SHADER, file!()),

View file

@ -11,7 +11,7 @@ fn my_runner(mut app: App) {
println!("Type stuff into the console");
for line in io::stdin().lines() {
{
let mut input = app.world.resource_mut::<Input>();
let mut input = app.world_mut().resource_mut::<Input>();
input.0 = line.unwrap();
}
app.update();

View file

@ -33,14 +33,14 @@ fn main() {
//
// Note that we modify `MainScheduleOrder` directly in `main` and not in a startup system. The reason for this is
// that the `MainScheduleOrder` cannot be modified from systems that are run as part of the `Main` schedule.
let mut main_schedule_order = app.world.resource_mut::<MainScheduleOrder>();
let mut main_schedule_order = app.world_mut().resource_mut::<MainScheduleOrder>();
main_schedule_order.insert_after(Update, SingleThreadedUpdate);
// Adding a custom startup schedule works similarly, but needs to use `insert_startup_after`
// instead of `insert_after`.
app.add_schedule(Schedule::new(CustomStartup));
let mut main_schedule_order = app.world.resource_mut::<MainScheduleOrder>();
let mut main_schedule_order = app.world_mut().resource_mut::<MainScheduleOrder>();
main_schedule_order.insert_startup_after(PreStartup, CustomStartup);
app.add_systems(SingleThreadedUpdate, single_threaded_update_system)

View file

@ -54,7 +54,7 @@ fn main() {
* Stepping::continue_frame() is called
* System has been configured to always run"#
);
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.add_schedule(Update).enable();
app.update();
@ -65,7 +65,7 @@ fn main() {
Stepping, step means run the next system across all the schedules
that have been added to the Stepping resource."#
);
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.step_frame();
app.update();
@ -89,7 +89,7 @@ fn main() {
case, we previously performed a step, running one system in Update.
This continue will cause all remaining systems in Update to run."#
);
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.continue_frame();
app.update();
@ -102,7 +102,7 @@ fn main() {
systems."#
);
for _ in 0..4 {
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.step_frame();
app.update();
}
@ -116,10 +116,10 @@ fn main() {
execute all systems in the frame. Stepping::always_run() allows
us to granularly allow systems to run when stepping is enabled."#
);
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.always_run(Update, update_system_two);
for _ in 0..3 {
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.step_frame();
app.update();
}
@ -132,7 +132,7 @@ fn main() {
Stepping::never_run() allows us to disable systems while Stepping
is enabled."#
);
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.never_run(Update, update_system_two);
stepping.continue_frame();
app.update();
@ -155,14 +155,14 @@ fn main() {
During the final continue pre_update_system() and
update_system_three() run."#
);
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.set_breakpoint(Update, update_system_two);
stepping.continue_frame();
app.update();
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.step_frame();
app.update();
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.continue_frame();
app.update();
@ -172,7 +172,7 @@ fn main() {
through all systems
Result: All systems will run"#
);
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.clear_breakpoint(Update, update_system_two);
stepping.continue_frame();
app.update();
@ -184,7 +184,7 @@ fn main() {
call Stepping::step_frame() or Stepping::continue_frame() to run
systems in the Update schedule."#
);
let mut stepping = app.world.resource_mut::<Stepping>();
let mut stepping = app.world_mut().resource_mut::<Stepping>();
stepping.disable();
app.update();
}

View file

@ -36,7 +36,7 @@ impl Plugin for SteppingPlugin {
// We need an independent schedule so we have access to all other
// schedules through the `Stepping` resource
app.init_schedule(DebugSchedule);
let mut order = app.world.resource_mut::<MainScheduleOrder>();
let mut order = app.world_mut().resource_mut::<MainScheduleOrder>();
order.insert_after(Update, DebugSchedule);
// create our stepping resource

View file

@ -84,7 +84,7 @@ impl Plugin for GameOfLifeComputePlugin {
prepare_bind_group.in_set(RenderSet::PrepareBindGroups),
);
let mut render_graph = render_app.world.resource_mut::<RenderGraph>();
let mut render_graph = render_app.world_mut().resource_mut::<RenderGraph>();
render_graph.add_node(GameOfLifeLabel, GameOfLifeNode::default());
render_graph.add_node_edge(GameOfLifeLabel, bevy::render::graph::CameraDriverLabel);
}

View file

@ -59,7 +59,7 @@ impl Plugin for PostProcessPlugin {
));
// We need to get the render app from the main app
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
@ -97,7 +97,7 @@ impl Plugin for PostProcessPlugin {
fn finish(&self, app: &mut App) {
// We need to get the render app from the main app
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -33,11 +33,11 @@ impl Plugin for GpuFeatureSupportChecker {
fn build(&self, _app: &mut App) {}
fn finish(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};
let render_device = render_app.world.resource::<RenderDevice>();
let render_device = render_app.world().resource::<RenderDevice>();
// Check if the device support the required feature. If not, exit the example.
// In a real application, you should setup a fallback for the missing feature

View file

@ -157,7 +157,7 @@ struct LogVisibleLights;
impl Plugin for LogVisibleLights {
fn build(&self, app: &mut App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
};

View file

@ -45,29 +45,29 @@ fn runner(mut app: App) {
}
"f" => {
println!("FAST: setting relative speed to 2x");
app.world
app.world_mut()
.resource_mut::<Time<Virtual>>()
.set_relative_speed(2.0);
}
"n" => {
println!("NORMAL: setting relative speed to 1x");
app.world
app.world_mut()
.resource_mut::<Time<Virtual>>()
.set_relative_speed(1.0);
}
"s" => {
println!("SLOW: setting relative speed to 0.5x");
app.world
app.world_mut()
.resource_mut::<Time<Virtual>>()
.set_relative_speed(0.5);
}
"p" => {
println!("PAUSE: pausing virtual clock");
app.world.resource_mut::<Time<Virtual>>().pause();
app.world_mut().resource_mut::<Time<Virtual>>().pause();
}
"u" => {
println!("UNPAUSE: resuming virtual clock");
app.world.resource_mut::<Time<Virtual>>().unpause();
app.world_mut().resource_mut::<Time<Virtual>>().unpause();
}
"q" => {
println!("QUITTING!");

View file

@ -62,7 +62,7 @@ fn did_hurt_enemy() {
// Setup test entities
let enemy_id = app
.world
.world_mut()
.spawn(Enemy {
hit_points: 5,
score_value: 3,
@ -73,8 +73,8 @@ fn did_hurt_enemy() {
app.update();
// Check resulting changes
assert!(app.world.get::<Enemy>(enemy_id).is_some());
assert_eq!(app.world.get::<Enemy>(enemy_id).unwrap().hit_points, 4);
assert!(app.world().get::<Enemy>(enemy_id).is_some());
assert_eq!(app.world().get::<Enemy>(enemy_id).unwrap().hit_points, 4);
}
#[test]
@ -93,7 +93,7 @@ fn did_despawn_enemy() {
// Setup test entities
let enemy_id = app
.world
.world_mut()
.spawn(Enemy {
hit_points: 1,
score_value: 1,
@ -104,10 +104,10 @@ fn did_despawn_enemy() {
app.update();
// Check enemy was despawned
assert!(app.world.get::<Enemy>(enemy_id).is_none());
assert!(app.world().get::<Enemy>(enemy_id).is_none());
// Get `EnemyDied` event reader
let enemy_died_events = app.world.resource::<Events<EnemyDied>>();
let enemy_died_events = app.world().resource::<Events<EnemyDied>>();
let mut enemy_died_reader = enemy_died_events.get_reader();
let enemy_died = enemy_died_reader.read(enemy_died_events).next().unwrap();
@ -132,16 +132,18 @@ fn spawn_enemy_using_input_resource() {
app.update();
// Check resulting changes, one entity has been spawned with `Enemy` component
assert_eq!(app.world.query::<&Enemy>().iter(&app.world).len(), 1);
assert_eq!(app.world_mut().query::<&Enemy>().iter(app.world()).len(), 1);
// Clear the `just_pressed` status for all `KeyCode`s
app.world.resource_mut::<ButtonInput<KeyCode>>().clear();
app.world_mut()
.resource_mut::<ButtonInput<KeyCode>>()
.clear();
// Run systems
app.update();
// Check resulting changes, no new entity has been spawned
assert_eq!(app.world.query::<&Enemy>().iter(&app.world).len(), 1);
assert_eq!(app.world_mut().query::<&Enemy>().iter(app.world()).len(), 1);
}
#[test]
@ -159,7 +161,7 @@ fn update_score_on_event() {
app.add_systems(Update, update_score);
// Send an `EnemyDied` event
app.world
app.world_mut()
.resource_mut::<Events<EnemyDied>>()
.send(EnemyDied(3));
@ -167,5 +169,5 @@ fn update_score_on_event() {
app.update();
// Check resulting changes
assert_eq!(app.world.resource::<Score>().0, 3);
assert_eq!(app.world().resource::<Score>().0, 3);
}