Make Resource trait opt-in, requiring #[derive(Resource)] V2 (#5577)

*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.

While ergonomic, this results in several drawbacks:

* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
 * Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
   *ira: My commits are not as well organized :')*
 * I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
 * I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.

## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.

## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.

If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.

`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.


Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
ira 2022-08-08 21:36:35 +00:00
parent 2ac744331b
commit 992681b59b
133 changed files with 805 additions and 525 deletions

View file

@ -1,6 +1,6 @@
use bevy_ecs::{
component::Component,
prelude::{ParallelSystemDescriptorCoercion, Res, RunCriteriaDescriptorCoercion},
prelude::{ParallelSystemDescriptorCoercion, Res, Resource, RunCriteriaDescriptorCoercion},
schedule::{ShouldRun, Stage, SystemStage},
system::Query,
world::World,
@ -136,7 +136,7 @@ pub fn run_criteria_no_with_labels(criterion: &mut Criterion) {
group.finish();
}
#[derive(Component)]
#[derive(Component, Resource)]
struct TestBool(pub bool);
pub fn run_criteria_yes_with_query(criterion: &mut Criterion) {

View file

@ -1,5 +1,6 @@
use crate::{CoreStage, Plugin, PluginGroup, PluginGroupBuilder, StartupSchedule, StartupStage};
pub use bevy_derive::AppLabel;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
event::{Event, Events},
prelude::{FromWorld, IntoExclusiveSystem},
@ -22,6 +23,11 @@ bevy_utils::define_label!(
AppLabelId,
);
/// The [`Resource`] that stores the [`App`]'s [`TypeRegistry`](bevy_reflect::TypeRegistry).
#[cfg(feature = "bevy_reflect")]
#[derive(Resource, Clone, Deref, DerefMut, Default)]
pub struct AppTypeRegistry(pub bevy_reflect::TypeRegistryArc);
#[allow(clippy::needless_doctest_main)]
/// A container of app logic and data.
///
@ -74,7 +80,7 @@ impl Default for App {
fn default() -> Self {
let mut app = App::empty();
#[cfg(feature = "bevy_reflect")]
app.init_resource::<bevy_reflect::TypeRegistryArc>();
app.init_resource::<AppTypeRegistry>();
app.add_default_stages()
.add_event::<AppExit>()
@ -647,7 +653,9 @@ impl App {
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// #[derive(Resource)]
/// struct MyCounter {
/// counter: usize,
/// }
@ -660,15 +668,16 @@ impl App {
self
}
/// Inserts a non-send [`Resource`] to the app.
/// Inserts a non-send resource to the app.
///
/// You usually want to use [`insert_resource`](Self::insert_resource),
/// but there are some special cases when a [`Resource`] cannot be sent across threads.
/// but there are some special cases when a resource cannot be sent across threads.
///
/// # Examples
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// struct MyCounter {
/// counter: usize,
@ -694,7 +703,9 @@ impl App {
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// #[derive(Resource)]
/// struct MyCounter {
/// counter: usize,
/// }
@ -873,7 +884,7 @@ impl App {
#[cfg(feature = "bevy_reflect")]
pub fn register_type<T: bevy_reflect::GetTypeRegistration>(&mut self) -> &mut Self {
{
let registry = self.world.resource_mut::<bevy_reflect::TypeRegistryArc>();
let registry = self.world.resource_mut::<AppTypeRegistry>();
registry.write().register::<T>();
}
self
@ -906,7 +917,7 @@ impl App {
&mut self,
) -> &mut Self {
{
let registry = self.world.resource_mut::<bevy_reflect::TypeRegistryArc>();
let registry = self.world.resource_mut::<AppTypeRegistry>();
registry.write().register_type_data::<T, D>();
}
self

View file

@ -1,6 +1,7 @@
use crate::{app::AppExit, App};
use serde::Deserialize;
use bevy_ecs::prelude::Resource;
use bevy_utils::tracing::info;
/// A configuration struct for automated CI testing.
@ -8,7 +9,7 @@ use bevy_utils::tracing::info;
/// It gets used when the `bevy_ci_testing` feature is enabled to automatically
/// exit a Bevy app when run through the CI. This is needed because otherwise
/// Bevy apps would be stuck in the game loop and wouldn't allow the CI to progress.
#[derive(Deserialize)]
#[derive(Deserialize, Resource)]
pub struct CiTestingConfig {
/// The number of frames after which Bevy should exit.
pub exit_after: Option<u32>,

View file

@ -18,6 +18,9 @@ pub use schedule_runner::*;
#[allow(missing_docs)]
pub mod prelude {
#[cfg(feature = "bevy_reflect")]
#[doc(hidden)]
pub use crate::AppTypeRegistry;
#[doc(hidden)]
pub use crate::{
app::App, CoreStage, DynamicPlugin, Plugin, PluginGroup, StartupSchedule, StartupStage,

View file

@ -3,6 +3,7 @@ use crate::{
plugin::Plugin,
};
use bevy_ecs::event::{Events, ManualEventReader};
use bevy_ecs::prelude::Resource;
use bevy_utils::{Duration, Instant};
#[cfg(target_arch = "wasm32")]
@ -34,7 +35,7 @@ impl Default for RunMode {
/// The configuration information for the [`ScheduleRunnerPlugin`].
///
/// It gets added as a [`Resource`](bevy_ecs::system::Resource) inside of the [`ScheduleRunnerPlugin`].
#[derive(Copy, Clone, Default)]
#[derive(Copy, Clone, Default, Resource)]
pub struct ScheduleRunnerSettings {
/// Determines whether the [`Schedule`](bevy_ecs::schedule::Schedule) is run once or repeatedly.
pub run_mode: RunMode,

View file

@ -5,7 +5,7 @@ use crate::{
RefChange, RefChangeChannel, SourceInfo, SourceMeta,
};
use anyhow::Result;
use bevy_ecs::system::{Res, ResMut};
use bevy_ecs::system::{Res, ResMut, Resource};
use bevy_log::warn;
use bevy_tasks::IoTaskPool;
use bevy_utils::{Entry, HashMap, Uuid};
@ -102,7 +102,7 @@ pub struct AssetServerInternal {
/// See the [`asset_loading`] example for more information.
///
/// [`asset_loading`]: https://github.com/bevyengine/bevy/tree/latest/examples/asset/asset_loading.rs
#[derive(Clone)]
#[derive(Clone, Resource)]
pub struct AssetServer {
pub(crate) server: Arc<AssetServerInternal>,
}

View file

@ -5,7 +5,7 @@ use crate::{
use bevy_app::App;
use bevy_ecs::{
event::{EventWriter, Events},
system::ResMut,
system::{ResMut, Resource},
world::FromWorld,
};
use bevy_utils::HashMap;
@ -66,7 +66,7 @@ impl<T: Asset> Debug for AssetEvent<T> {
/// Remember, if there are no Strong handles for an asset (i.e. they have all been dropped), the
/// asset will unload. Make sure you always have a Strong handle when you want to keep an asset
/// loaded!
#[derive(Debug)]
#[derive(Debug, Resource)]
pub struct Assets<T: Asset> {
assets: HashMap<HandleId, T>,
events: Events<AssetEvent<T>>,

View file

@ -6,7 +6,7 @@ use bevy_app::{App, Plugin};
use bevy_ecs::{
event::Events,
schedule::SystemLabel,
system::{NonSendMut, Res, ResMut, SystemState},
system::{NonSendMut, Res, ResMut, Resource, SystemState},
};
use bevy_tasks::{IoTaskPool, TaskPoolBuilder};
use bevy_utils::HashMap;
@ -52,6 +52,7 @@ pub struct DebugAssetServerPlugin;
/// A collection that maps internal assets in a [`DebugAssetApp`]'s asset server to their mirrors in
/// the main [`App`].
#[derive(Resource)]
pub struct HandleMap<T: Asset> {
/// The collection of asset handles.
pub handles: HashMap<Handle<T>, Handle<T>>,

View file

@ -43,7 +43,10 @@ pub use loader::*;
pub use path::*;
use bevy_app::{prelude::Plugin, App};
use bevy_ecs::schedule::{StageLabel, SystemStage};
use bevy_ecs::{
schedule::{StageLabel, SystemStage},
system::Resource,
};
/// The names of asset stages in an [`App`] schedule.
#[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)]
@ -62,6 +65,7 @@ pub enum AssetStage {
pub struct AssetPlugin;
/// [`AssetServer`] settings.
#[derive(Resource)]
pub struct AssetServerSettings {
/// The base folder where assets are loaded from, relative to the executable.
pub asset_folder: String,

View file

@ -1,9 +1,10 @@
use crate::{AudioSink, AudioSource, Decodable};
use bevy_asset::{Asset, Handle, HandleId};
use bevy_ecs::system::Resource;
use parking_lot::RwLock;
use std::{collections::VecDeque, fmt};
/// Use this resource to play audio
/// Use this [`Resource`] to play audio.
///
/// ```
/// # use bevy_ecs::system::Res;
@ -13,6 +14,7 @@ use std::{collections::VecDeque, fmt};
/// audio.play(asset_server.load("my_sound.ogg"));
/// }
/// ```
#[derive(Resource)]
pub struct Audio<Source = AudioSource>
where
Source: Asset + Decodable,

View file

@ -1,3 +1,4 @@
use bevy_ecs::prelude::Resource;
use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder};
use bevy_utils::tracing::trace;
@ -33,7 +34,7 @@ impl TaskPoolThreadAssignmentPolicy {
/// Helper for configuring and creating the default task pools. For end-users who want full control,
/// insert the default task pools into the resource map manually. If the pools are already inserted,
/// this helper will do nothing.
#[derive(Clone)]
#[derive(Clone, Resource)]
pub struct DefaultTaskPoolOptions {
/// If the number of physical cores is less than min_total_threads, force using
/// min_total_threads

View file

@ -13,11 +13,11 @@ pub enum ClearColorConfig {
None,
}
/// When used as a resource, sets the color that is used to clear the screen between frames.
/// A [`Resource`] that stores the color that is used to clear the screen between frames.
///
/// This color appears as the "background" color for simple apps, when
/// there are portions of the screen with nothing rendered.
#[derive(Component, Clone, Debug, Deref, DerefMut, ExtractResource, Reflect)]
/// This color appears as the "background" color for simple apps,
/// when there are portions of the screen with nothing rendered.
#[derive(Resource, Clone, Debug, Deref, DerefMut, ExtractResource, Reflect)]
#[reflect(Resource)]
pub struct ClearColor(pub Color);

View file

@ -1,3 +1,4 @@
use bevy_ecs::system::Resource;
use bevy_log::warn;
use bevy_utils::{Duration, Instant, StableHashMap, Uuid};
use std::{borrow::Cow, collections::VecDeque};
@ -154,7 +155,7 @@ impl Diagnostic {
}
/// A collection of [Diagnostic]s
#[derive(Debug, Default)]
#[derive(Debug, Default, Resource)]
pub struct Diagnostics {
// This uses a [`StableHashMap`] to ensure that the iteration order is deterministic between
// runs when all diagnostics are inserted in the same order.

View file

@ -1,12 +1,13 @@
use crate::{Diagnostic, DiagnosticId, Diagnostics};
use bevy_app::prelude::*;
use bevy_ecs::system::{Res, ResMut};
use bevy_ecs::system::{Res, ResMut, Resource};
use bevy_time::Time;
/// Adds "frame time" diagnostic to an App, specifically "frame time", "fps" and "frame count"
#[derive(Default)]
pub struct FrameTimeDiagnosticsPlugin;
#[derive(Resource)]
pub struct FrameTimeDiagnosticsState {
frame_count: u64,
}

View file

@ -1,6 +1,6 @@
use super::{Diagnostic, DiagnosticId, Diagnostics};
use bevy_app::prelude::*;
use bevy_ecs::system::{Res, ResMut};
use bevy_ecs::system::{Res, ResMut, Resource};
use bevy_log::{debug, info};
use bevy_time::{Time, Timer};
use bevy_utils::Duration;
@ -13,6 +13,7 @@ pub struct LogDiagnosticsPlugin {
}
/// State used by the [`LogDiagnosticsPlugin`]
#[derive(Resource)]
struct LogDiagnosticsState {
timer: Timer,
filter: Option<Vec<DiagnosticId>>,

View file

@ -91,7 +91,7 @@ Apps often require unique resources, such as asset collections, renderers, audio
```rust
use bevy_ecs::prelude::*;
#[derive(Default)]
#[derive(Resource, Default)]
struct Time {
seconds: f32,
}
@ -213,6 +213,7 @@ Resources also expose change state:
```rust
use bevy_ecs::prelude::*;
#[derive(Resource)]
struct Time(f32);
// Prints "time changed!" if the Time resource has changed since the last run of the System

View file

@ -40,7 +40,7 @@ fn main() {
}
// This struct will be used as a Resource keeping track of the total amount of spawned entities
#[derive(Debug)]
#[derive(Debug, Resource)]
struct EntityCounter {
pub value: i32,
}

View file

@ -27,7 +27,7 @@ fn main() {
}
// Counter resource to be increased and read by systems
#[derive(Debug)]
#[derive(Debug, Resource)]
struct Counter {
pub value: i32,
}

View file

@ -4,6 +4,24 @@ use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{quote, ToTokens};
use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident, Path, Result};
pub fn derive_resource(input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as DeriveInput);
let bevy_ecs_path: Path = crate::bevy_ecs_path();
ast.generics
.make_where_clause()
.predicates
.push(parse_quote! { Self: Send + Sync + 'static });
let struct_name = &ast.ident;
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
TokenStream::from(quote! {
impl #impl_generics #bevy_ecs_path::system::Resource for #struct_name #type_generics #where_clause {
}
})
}
pub fn derive_component(input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as DeriveInput);
let bevy_ecs_path: Path = crate::bevy_ecs_path();

View file

@ -496,6 +496,11 @@ pub(crate) fn bevy_ecs_path() -> syn::Path {
BevyManifest::default().get_path("bevy_ecs")
}
#[proc_macro_derive(Resource)]
pub fn derive_resource(input: TokenStream) -> TokenStream {
component::derive_resource(input)
}
#[proc_macro_derive(Component, attributes(component))]
pub fn derive_component(input: TokenStream) -> TokenStream {
component::derive_component(input)

View file

@ -31,6 +31,7 @@ pub const MAX_CHANGE_AGE: u32 = u32::MAX - (2 * CHECK_TICK_THRESHOLD - 1);
/// ```
/// use bevy_ecs::prelude::*;
///
/// #[derive(Resource)]
/// struct MyResource(u32);
///
/// fn my_system(mut resource: ResMut<MyResource>) {
@ -165,9 +166,9 @@ pub(crate) struct Ticks<'a> {
pub(crate) change_tick: u32,
}
/// Unique mutable borrow of a resource.
/// Unique mutable borrow of a [`Resource`].
///
/// See the [`World`](crate::world::World) documentation to see the usage of a resource.
/// See the [`Resource`] documentation for usage.
///
/// If you need a shared borrow, use [`Res`](crate::system::Res) instead.
///
@ -306,6 +307,8 @@ impl std::fmt::Debug for MutUntyped<'_> {
#[cfg(test)]
mod tests {
use bevy_ecs_macros::Resource;
use crate::{
self as bevy_ecs,
change_detection::{
@ -320,7 +323,8 @@ mod tests {
#[derive(Component)]
struct C;
struct R; // Resource
#[derive(Resource)]
struct R;
#[test]
fn change_expiration() {

View file

@ -1,7 +1,7 @@
//! Event handling types.
use crate as bevy_ecs;
use crate::system::{Local, Res, ResMut, SystemParam};
use crate::system::{Local, Res, ResMut, Resource, SystemParam};
use bevy_utils::tracing::trace;
use std::ops::{Deref, DerefMut};
use std::{
@ -128,7 +128,7 @@ struct EventInstance<E: Event> {
/// [Example usage.](https://github.com/bevyengine/bevy/blob/latest/examples/ecs/event.rs)
/// [Example usage standalone.](https://github.com/bevyengine/bevy/blob/latest/crates/bevy_ecs/examples/events.rs)
///
#[derive(Debug)]
#[derive(Debug, Resource)]
pub struct Events<E: Event> {
/// Holds the oldest still active events.
/// Note that a.start_event_count + a.len() should always === events_b.start_event_count.

View file

@ -40,8 +40,8 @@ pub mod prelude {
},
system::{
Commands, In, IntoChainSystem, IntoExclusiveSystem, IntoSystem, Local, NonSend,
NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut, System,
SystemParamFunction,
NonSendMut, ParallelCommands, ParamSet, Query, RemovedComponents, Res, ResMut,
Resource, System, SystemParamFunction,
},
world::{FromWorld, Mut, World},
};
@ -58,6 +58,7 @@ mod tests {
component::{Component, ComponentId},
entity::Entity,
query::{Added, ChangeTrackers, Changed, FilteredAccess, With, Without, WorldQuery},
system::Resource,
world::{Mut, World},
};
use bevy_tasks::{ComputeTaskPool, TaskPool};
@ -69,7 +70,7 @@ mod tests {
},
};
#[derive(Component, Debug, PartialEq, Eq, Clone, Copy)]
#[derive(Component, Resource, Debug, PartialEq, Eq, Clone, Copy)]
struct A(usize);
#[derive(Component, Debug, PartialEq, Eq, Clone, Copy)]
struct B(usize);
@ -1003,16 +1004,24 @@ mod tests {
#[test]
fn resource() {
let mut world = World::default();
assert!(world.get_resource::<i32>().is_none());
assert!(!world.contains_resource::<i32>());
assert!(!world.is_resource_added::<i32>());
assert!(!world.is_resource_changed::<i32>());
use crate::system::Resource;
world.insert_resource(123);
#[derive(Resource, PartialEq, Debug)]
struct Num(i32);
#[derive(Resource, PartialEq, Debug)]
struct BigNum(u64);
let mut world = World::default();
assert!(world.get_resource::<Num>().is_none());
assert!(!world.contains_resource::<Num>());
assert!(!world.is_resource_added::<Num>());
assert!(!world.is_resource_changed::<Num>());
world.insert_resource(Num(123));
let resource_id = world
.components()
.get_resource_id(TypeId::of::<i32>())
.get_resource_id(TypeId::of::<Num>())
.unwrap();
let archetype_component_id = world
.archetypes()
@ -1020,61 +1029,61 @@ mod tests {
.get_archetype_component_id(resource_id)
.unwrap();
assert_eq!(*world.resource::<i32>(), 123);
assert!(world.contains_resource::<i32>());
assert!(world.is_resource_added::<i32>());
assert!(world.is_resource_changed::<i32>());
assert_eq!(world.resource::<Num>().0, 123);
assert!(world.contains_resource::<Num>());
assert!(world.is_resource_added::<Num>());
assert!(world.is_resource_changed::<Num>());
world.insert_resource(456u64);
assert_eq!(*world.resource::<u64>(), 456u64);
world.insert_resource(BigNum(456));
assert_eq!(world.resource::<BigNum>().0, 456u64);
world.insert_resource(789u64);
assert_eq!(*world.resource::<u64>(), 789);
world.insert_resource(BigNum(789));
assert_eq!(world.resource::<BigNum>().0, 789);
{
let mut value = world.resource_mut::<u64>();
assert_eq!(*value, 789);
*value = 10;
let mut value = world.resource_mut::<BigNum>();
assert_eq!(value.0, 789);
value.0 = 10;
}
assert_eq!(
world.resource::<u64>(),
&10,
world.resource::<BigNum>().0,
10,
"resource changes are preserved"
);
assert_eq!(
world.remove_resource::<u64>(),
Some(10),
world.remove_resource::<BigNum>(),
Some(BigNum(10)),
"removed resource has the correct value"
);
assert_eq!(
world.get_resource::<u64>(),
world.get_resource::<BigNum>(),
None,
"removed resource no longer exists"
);
assert_eq!(
world.remove_resource::<u64>(),
world.remove_resource::<BigNum>(),
None,
"double remove returns nothing"
);
world.insert_resource(1u64);
world.insert_resource(BigNum(1));
assert_eq!(
world.get_resource::<u64>(),
Some(&1u64),
world.get_resource::<BigNum>(),
Some(&BigNum(1)),
"re-inserting resources works"
);
assert_eq!(
world.get_resource::<i32>(),
Some(&123),
world.get_resource::<Num>(),
Some(&Num(123)),
"other resources are unaffected"
);
let current_resource_id = world
.components()
.get_resource_id(TypeId::of::<i32>())
.get_resource_id(TypeId::of::<Num>())
.unwrap();
assert_eq!(
resource_id, current_resource_id,
@ -1120,7 +1129,7 @@ mod tests {
assert_eq!(
e.get::<A>(),
None,
"i32 is in the removed bundle, so it should not exist"
"Num is in the removed bundle, so it should not exist"
);
assert_eq!(
e.get::<B>(),
@ -1325,12 +1334,12 @@ mod tests {
#[test]
fn resource_scope() {
let mut world = World::default();
world.insert_resource::<i32>(0);
world.resource_scope(|world: &mut World, mut value: Mut<i32>| {
*value += 1;
assert!(!world.contains_resource::<i32>());
world.insert_resource(A(0));
world.resource_scope(|world: &mut World, mut value: Mut<A>| {
value.0 += 1;
assert!(!world.contains_resource::<A>());
});
assert_eq!(*world.resource::<i32>(), 1);
assert_eq!(world.resource::<A>().0, 1);
}
#[test]
@ -1367,7 +1376,7 @@ mod tests {
fn clear_entities() {
let mut world = World::default();
world.insert_resource::<i32>(0);
world.insert_resource(A(0));
world.spawn().insert(A(1));
world.spawn().insert(SparseStored(1));
@ -1396,7 +1405,7 @@ mod tests {
"world should not have any entities"
);
assert_eq!(
*world.resource::<i32>(),
world.resource::<A>().0,
0,
"world should still contain resources"
);

View file

@ -11,7 +11,7 @@ use bevy_utils::tracing::Instrument;
use fixedbitset::FixedBitSet;
#[cfg(test)]
use SchedulingEvent::*;
use scheduling_event::*;
struct SystemSchedulingMetadata {
/// Used to signal the system's task to start the system.
@ -107,7 +107,7 @@ impl ParallelSystemExecutor for ParallelExecutor {
#[cfg(test)]
if self.events_sender.is_none() {
let (sender, receiver) = async_channel::unbounded::<SchedulingEvent>();
world.insert_resource(receiver);
world.insert_resource(SchedulingEvents(receiver));
self.events_sender = Some(sender);
}
@ -260,7 +260,7 @@ impl ParallelExecutor {
}
#[cfg(test)]
if started_systems != 0 {
self.emit_event(StartedSystems(started_systems));
self.emit_event(SchedulingEvent::StartedSystems(started_systems));
}
// Remove now running systems from the queue.
self.queued.difference_with(&self.running);
@ -308,29 +308,43 @@ impl ParallelExecutor {
}
#[cfg(test)]
#[derive(Debug, PartialEq, Eq)]
enum SchedulingEvent {
StartedSystems(usize),
mod scheduling_event {
use crate as bevy_ecs;
use crate::system::Resource;
use async_channel::Receiver;
#[derive(Debug, PartialEq, Eq)]
pub(super) enum SchedulingEvent {
StartedSystems(usize),
}
#[derive(Resource)]
pub(super) struct SchedulingEvents(pub(crate) Receiver<SchedulingEvent>);
}
#[cfg(test)]
#[cfg(test)]
mod tests {
use super::SchedulingEvent::{self, *};
use crate::{
schedule::{SingleThreadedExecutor, Stage, SystemStage},
system::{NonSend, Query, Res, ResMut},
self as bevy_ecs,
component::Component,
schedule::{
executor_parallel::scheduling_event::*, SingleThreadedExecutor, Stage, SystemStage,
},
system::{NonSend, Query, Res, ResMut, Resource},
world::World,
};
use async_channel::Receiver;
use crate as bevy_ecs;
use crate::component::Component;
use SchedulingEvent::StartedSystems;
#[derive(Component)]
struct W<T>(T);
#[derive(Resource, Default)]
struct Counter(usize);
fn receive_events(world: &World) -> Vec<SchedulingEvent> {
let mut events = Vec::new();
while let Ok(event) = world.resource::<Receiver<SchedulingEvent>>().try_recv() {
while let Ok(event) = world.resource::<SchedulingEvents>().0.try_recv() {
events.push(event);
}
events
@ -355,9 +369,9 @@ mod tests {
#[test]
fn resources() {
let mut world = World::new();
world.insert_resource(0usize);
fn wants_mut(_: ResMut<usize>) {}
fn wants_ref(_: Res<usize>) {}
world.init_resource::<Counter>();
fn wants_mut(_: ResMut<Counter>) {}
fn wants_ref(_: Res<Counter>) {}
let mut stage = SystemStage::parallel()
.with_system(wants_mut)
.with_system(wants_mut);

View file

@ -1,4 +1,5 @@
use crate::{
self as bevy_ecs,
change_detection::CHECK_TICK_THRESHOLD,
component::ComponentId,
prelude::IntoSystem,
@ -12,6 +13,7 @@ use crate::{
},
world::{World, WorldId},
};
use bevy_ecs_macros::Resource;
use bevy_utils::{
tracing::{info, warn},
HashMap, HashSet,
@ -50,7 +52,7 @@ impl_downcast!(Stage);
///
/// The checker may report a system more times than the amount of constraints it would actually need
/// to have unambiguous order with regards to a group of already-constrained systems.
#[derive(Default)]
#[derive(Resource, Default)]
pub struct ReportExecutionOrderAmbiguities;
/// Stores and executes systems. Execution order is not defined unless explicitly specified;
@ -982,15 +984,22 @@ mod tests {
use crate as bevy_ecs;
use crate::component::Component;
use crate::system::Resource;
#[derive(Component)]
struct W<T>(T);
#[derive(Resource)]
struct R(usize);
#[derive(Resource, Default)]
struct EntityCount(Vec<usize>);
fn make_exclusive(tag: usize) -> impl FnMut(&mut World) {
move |world| world.resource_mut::<Vec<usize>>().push(tag)
move |world| world.resource_mut::<EntityCount>().0.push(tag)
}
fn make_parallel(tag: usize) -> impl FnMut(ResMut<Vec<usize>>) {
move |mut resource: ResMut<Vec<usize>>| resource.push(tag)
fn make_parallel(tag: usize) -> impl FnMut(ResMut<EntityCount>) {
move |mut resource: ResMut<EntityCount>| resource.0.push(tag)
}
fn every_other_time(mut has_ran: Local<bool>) -> ShouldRun {
@ -1005,48 +1014,48 @@ mod tests {
#[test]
fn insertion_points() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(0).exclusive_system().at_start())
.with_system(make_parallel(1))
.with_system(make_exclusive(2).exclusive_system().before_commands())
.with_system(make_exclusive(3).exclusive_system().at_end());
stage.run(&mut world);
assert_eq!(*world.resource_mut::<Vec<usize>>(), vec![0, 1, 2, 3]);
assert_eq!(world.resource_mut::<EntityCount>().0, vec![0, 1, 2, 3]);
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 0, 1, 2, 3]
);
world.resource_mut::<Vec<usize>>().clear();
world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(2).exclusive_system().before_commands())
.with_system(make_exclusive(3).exclusive_system().at_end())
.with_system(make_parallel(1))
.with_system(make_exclusive(0).exclusive_system().at_start());
stage.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 1, 2, 3]);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 3]);
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 0, 1, 2, 3]
);
world.resource_mut::<Vec<usize>>().clear();
world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel()
.with_system(make_parallel(2).exclusive_system().before_commands())
.with_system(make_parallel(3).exclusive_system().at_end())
.with_system(make_parallel(1))
.with_system(make_parallel(0).exclusive_system().at_start());
stage.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 1, 2, 3]);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 3]);
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 0, 1, 2, 3]
);
}
@ -1054,7 +1063,7 @@ mod tests {
#[test]
fn exclusive_after() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(1).exclusive_system().label("1").after("0"))
.with_system(make_exclusive(2).exclusive_system().after("1"))
@ -1062,13 +1071,13 @@ mod tests {
stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 1, 2, 0, 1, 2]);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 0, 1, 2]);
}
#[test]
fn exclusive_before() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(1).exclusive_system().label("1").before("2"))
.with_system(make_exclusive(2).exclusive_system().label("2"))
@ -1076,13 +1085,13 @@ mod tests {
stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 1, 2, 0, 1, 2]);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 0, 1, 2]);
}
#[test]
fn exclusive_mixed() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(2).exclusive_system().label("2"))
.with_system(make_exclusive(1).exclusive_system().after("0").before("2"))
@ -1093,7 +1102,7 @@ mod tests {
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
);
}
@ -1101,7 +1110,7 @@ mod tests {
#[test]
fn exclusive_multiple_labels() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(
make_exclusive(1)
@ -1119,9 +1128,9 @@ mod tests {
stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 1, 2, 0, 1, 2]);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 0, 1, 2]);
world.resource_mut::<Vec<usize>>().clear();
world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(2).exclusive_system().after("01").label("2"))
.with_system(make_exclusive(1).exclusive_system().label("01").after("0"))
@ -1132,11 +1141,11 @@ mod tests {
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
);
world.resource_mut::<Vec<usize>>().clear();
world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(2).exclusive_system().label("234").label("2"))
.with_system(
@ -1158,7 +1167,7 @@ mod tests {
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
);
}
@ -1166,7 +1175,7 @@ mod tests {
#[test]
fn exclusive_redundant_constraints() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(
make_exclusive(2)
@ -1197,7 +1206,7 @@ mod tests {
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
);
}
@ -1205,7 +1214,7 @@ mod tests {
#[test]
fn exclusive_mixed_across_sets() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(2).exclusive_system().label("2"))
.with_system_set(
@ -1219,7 +1228,7 @@ mod tests {
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
);
}
@ -1227,7 +1236,7 @@ mod tests {
#[test]
fn exclusive_run_criteria() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(0).exclusive_system().before("1"))
.with_system_set(
@ -1242,7 +1251,7 @@ mod tests {
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 0, 2, 0, 1, 2, 0, 2]
);
}
@ -1251,7 +1260,7 @@ mod tests {
#[should_panic]
fn exclusive_cycle_1() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(0).exclusive_system().label("0").after("0"));
stage.run(&mut world);
@ -1261,7 +1270,7 @@ mod tests {
#[should_panic]
fn exclusive_cycle_2() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(0).exclusive_system().label("0").after("1"))
.with_system(make_exclusive(1).exclusive_system().label("1").after("0"));
@ -1272,7 +1281,7 @@ mod tests {
#[should_panic]
fn exclusive_cycle_3() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_exclusive(0).exclusive_system().label("0"))
.with_system(make_exclusive(1).exclusive_system().after("0").before("2"))
@ -1283,7 +1292,7 @@ mod tests {
#[test]
fn parallel_after() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_parallel(1).after("0").label("1"))
.with_system(make_parallel(2).after("1"))
@ -1291,13 +1300,13 @@ mod tests {
stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 1, 2, 0, 1, 2]);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 0, 1, 2]);
}
#[test]
fn parallel_before() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_parallel(1).label("1").before("2"))
.with_system(make_parallel(2).label("2"))
@ -1305,13 +1314,13 @@ mod tests {
stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 1, 2, 0, 1, 2]);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 0, 1, 2]);
}
#[test]
fn parallel_mixed() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_parallel(2).label("2"))
.with_system(make_parallel(1).after("0").before("2"))
@ -1322,7 +1331,7 @@ mod tests {
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
);
}
@ -1330,7 +1339,7 @@ mod tests {
#[test]
fn parallel_multiple_labels() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_parallel(1).label("first").after("0"))
.with_system(make_parallel(2).after("first"))
@ -1338,9 +1347,9 @@ mod tests {
stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 1, 2, 0, 1, 2]);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 2, 0, 1, 2]);
world.resource_mut::<Vec<usize>>().clear();
world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel()
.with_system(make_parallel(2).after("01").label("2"))
.with_system(make_parallel(1).label("01").after("0"))
@ -1351,11 +1360,11 @@ mod tests {
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
);
world.resource_mut::<Vec<usize>>().clear();
world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel()
.with_system(make_parallel(2).label("234").label("2"))
.with_system(make_parallel(1).before("234").after("0"))
@ -1366,7 +1375,7 @@ mod tests {
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
);
}
@ -1374,7 +1383,7 @@ mod tests {
#[test]
fn parallel_redundant_constraints() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(
make_parallel(2)
@ -1400,7 +1409,7 @@ mod tests {
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
);
}
@ -1408,7 +1417,7 @@ mod tests {
#[test]
fn parallel_mixed_across_sets() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_parallel(2).label("2"))
.with_system_set(
@ -1422,7 +1431,7 @@ mod tests {
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
);
}
@ -1431,7 +1440,7 @@ mod tests {
fn parallel_run_criteria() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(
make_parallel(0)
@ -1444,9 +1453,9 @@ mod tests {
stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 1, 1, 0, 1, 1]);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 1, 1, 0, 1, 1]);
world.resource_mut::<Vec<usize>>().clear();
world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel()
.with_system(make_parallel(0).before("1"))
.with_system_set(
@ -1461,12 +1470,12 @@ mod tests {
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 0, 2, 0, 1, 2, 0, 2]
);
// Reusing criteria.
world.resource_mut::<Vec<usize>>().clear();
world.resource_mut::<EntityCount>().0.clear();
let mut stage = SystemStage::parallel()
.with_system_run_criteria(every_other_time.label("every other time"))
.with_system(make_parallel(0).before("1"))
@ -1488,13 +1497,13 @@ mod tests {
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 0, 3, 0, 1, 2, 3, 0, 3]
);
assert_eq!(stage.run_criteria.len(), 1);
// Piping criteria.
world.resource_mut::<Vec<usize>>().clear();
world.resource_mut::<EntityCount>().0.clear();
fn eot_piped(input: In<ShouldRun>, has_ran: Local<bool>) -> ShouldRun {
if let ShouldRun::Yes | ShouldRun::YesAndCheckAgain = input.0 {
every_other_time(has_ran)
@ -1529,13 +1538,13 @@ mod tests {
stage.run(&mut world);
}
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 4, 0, 0, 1, 0, 0, 1, 2, 3, 4, 0, 0, 1, 0, 0, 1, 2, 3, 4]
);
assert_eq!(stage.run_criteria.len(), 3);
// Discarding extra criteria with matching labels.
world.resource_mut::<Vec<usize>>().clear();
world.resource_mut::<EntityCount>().0.clear();
let mut stage =
SystemStage::parallel()
.with_system(make_parallel(0).before("1"))
@ -1552,7 +1561,7 @@ mod tests {
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(
*world.resource::<Vec<usize>>(),
world.resource::<EntityCount>().0,
vec![0, 1, 2, 3, 0, 3, 0, 1, 2, 3, 0, 3]
);
assert_eq!(stage.run_criteria.len(), 1);
@ -1572,7 +1581,7 @@ mod tests {
#[should_panic]
fn parallel_cycle_1() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel().with_system(make_parallel(0).label("0").after("0"));
stage.run(&mut world);
}
@ -1581,7 +1590,7 @@ mod tests {
#[should_panic]
fn parallel_cycle_2() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_parallel(0).label("0").after("1"))
.with_system(make_parallel(1).label("1").after("0"));
@ -1593,7 +1602,7 @@ mod tests {
fn parallel_cycle_3() {
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(make_parallel(0).label("0"))
.with_system(make_parallel(1).after("0").before("2"))
@ -1628,7 +1637,7 @@ mod tests {
}
fn empty() {}
fn resource(_: ResMut<usize>) {}
fn resource(_: ResMut<R>) {}
fn component(_: Query<&mut W<f32>>) {}
let mut world = World::new();
@ -1998,47 +2007,41 @@ mod tests {
#[test]
fn archetype_update_single_executor() {
fn query_count_system(
mut entity_count: ResMut<usize>,
query: Query<crate::entity::Entity>,
) {
*entity_count = query.iter().count();
fn query_count_system(mut entity_count: ResMut<R>, query: Query<crate::entity::Entity>) {
*entity_count = R(query.iter().count());
}
let mut world = World::new();
world.insert_resource(0_usize);
world.insert_resource(R(0));
let mut stage = SystemStage::single(query_count_system);
let entity = world.spawn().insert_bundle(()).id();
stage.run(&mut world);
assert_eq!(*world.resource::<usize>(), 1);
assert_eq!(world.resource::<R>().0, 1);
world.get_entity_mut(entity).unwrap().insert(W(1));
stage.run(&mut world);
assert_eq!(*world.resource::<usize>(), 1);
assert_eq!(world.resource::<R>().0, 1);
}
#[test]
fn archetype_update_parallel_executor() {
fn query_count_system(
mut entity_count: ResMut<usize>,
query: Query<crate::entity::Entity>,
) {
*entity_count = query.iter().count();
fn query_count_system(mut entity_count: ResMut<R>, query: Query<crate::entity::Entity>) {
*entity_count = R(query.iter().count());
}
let mut world = World::new();
world.insert_resource(0_usize);
world.insert_resource(R(0));
let mut stage = SystemStage::parallel();
stage.add_system(query_count_system);
let entity = world.spawn().insert_bundle(()).id();
stage.run(&mut world);
assert_eq!(*world.resource::<usize>(), 1);
assert_eq!(world.resource::<R>().0, 1);
world.get_entity_mut(entity).unwrap().insert(W(1));
stage.run(&mut world);
assert_eq!(*world.resource::<usize>(), 1);
assert_eq!(world.resource::<R>().0, 1);
}
#[test]
@ -2060,12 +2063,12 @@ mod tests {
commands.spawn().insert(Foo);
}
fn count_entities(query: Query<&Foo>, mut res: ResMut<Vec<usize>>) {
res.push(query.iter().len());
fn count_entities(query: Query<&Foo>, mut res: ResMut<EntityCount>) {
res.0.push(query.iter().len());
}
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage = SystemStage::parallel()
.with_system(spawn_entity.label("spawn"))
.with_system_set(
@ -2077,7 +2080,7 @@ mod tests {
stage.run(&mut world);
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 2]);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 2]);
}
#[test]
@ -2099,12 +2102,12 @@ mod tests {
commands.spawn().insert(Foo);
}
fn count_entities(query: Query<&Foo>, mut res: ResMut<Vec<usize>>) {
res.push(query.iter().len());
fn count_entities(query: Query<&Foo>, mut res: ResMut<EntityCount>) {
res.0.push(query.iter().len());
}
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<EntityCount>();
let mut stage_spawn = SystemStage::parallel().with_system(spawn_entity);
let mut stage_count = SystemStage::parallel()
.with_run_criteria(even_number_of_entities_critiera)
@ -2117,6 +2120,6 @@ mod tests {
stage_spawn.run(&mut world);
stage_count.run(&mut world);
stage_spawn.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 2]);
assert_eq!(world.resource::<EntityCount>().0, vec![0, 2]);
}
}

View file

@ -3,13 +3,15 @@ use crate::{
RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun,
SystemSet,
},
system::{In, IntoChainSystem, Local, Res, ResMut},
system::{In, IntoChainSystem, Local, Res, ResMut, Resource},
};
use std::{
any::TypeId,
fmt::{self, Debug},
hash::Hash,
};
// Required for derive macros
use crate as bevy_ecs;
pub trait StateData: Send + Sync + Clone + Eq + Debug + Hash + 'static {}
impl<T> StateData for T where T: Send + Sync + Clone + Eq + Debug + Hash + 'static {}
@ -21,7 +23,7 @@ impl<T> StateData for T where T: Send + Sync + Clone + Eq + Debug + Hash + 'stat
/// * Pop removes the current state, and unpauses the last paused state
/// * Set replaces the active state with a new one
/// * Replace unwinds the state stack, and replaces the entire stack with a single new state
#[derive(Debug)]
#[derive(Debug, Resource)]
pub struct State<T: StateData> {
transition: Option<StateTransition<T>>,
/// The current states in the stack.
@ -489,9 +491,12 @@ mod test {
#[test]
fn state_test() {
#[derive(Resource, Default)]
struct NameList(Vec<&'static str>);
let mut world = World::default();
world.insert_resource(Vec::<&'static str>::new());
world.init_resource::<NameList>();
world.insert_resource(State::new(MyState::S1));
let mut stage = SystemStage::parallel();
@ -500,55 +505,55 @@ mod test {
stage
.add_system_set(
State::on_enter_set(MyState::S1)
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("startup")),
.with_system(|mut r: ResMut<NameList>| r.0.push("startup")),
)
.add_system_set(State::on_update_set(MyState::S1).with_system(
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S1");
|mut r: ResMut<NameList>, mut s: ResMut<State<MyState>>| {
r.0.push("update S1");
s.overwrite_replace(MyState::S2).unwrap();
},
))
.add_system_set(
State::on_enter_set(MyState::S2)
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("enter S2")),
.with_system(|mut r: ResMut<NameList>| r.0.push("enter S2")),
)
.add_system_set(State::on_update_set(MyState::S2).with_system(
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S2");
|mut r: ResMut<NameList>, mut s: ResMut<State<MyState>>| {
r.0.push("update S2");
s.overwrite_replace(MyState::S3).unwrap();
},
))
.add_system_set(
State::on_exit_set(MyState::S2)
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("exit S2")),
.with_system(|mut r: ResMut<NameList>| r.0.push("exit S2")),
)
.add_system_set(
State::on_enter_set(MyState::S3)
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("enter S3")),
.with_system(|mut r: ResMut<NameList>| r.0.push("enter S3")),
)
.add_system_set(State::on_update_set(MyState::S3).with_system(
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S3");
|mut r: ResMut<NameList>, mut s: ResMut<State<MyState>>| {
r.0.push("update S3");
s.overwrite_push(MyState::S4).unwrap();
},
))
.add_system_set(
State::on_pause_set(MyState::S3)
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("pause S3")),
.with_system(|mut r: ResMut<NameList>| r.0.push("pause S3")),
)
.add_system_set(State::on_update_set(MyState::S4).with_system(
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S4");
|mut r: ResMut<NameList>, mut s: ResMut<State<MyState>>| {
r.0.push("update S4");
s.overwrite_push(MyState::S5).unwrap();
},
))
.add_system_set(State::on_inactive_update_set(MyState::S4).with_system(
(|mut r: ResMut<Vec<&'static str>>| r.push("inactive S4")).label("inactive s4"),
(|mut r: ResMut<NameList>| r.0.push("inactive S4")).label("inactive s4"),
))
.add_system_set(
State::on_update_set(MyState::S5).with_system(
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S5");
(|mut r: ResMut<NameList>, mut s: ResMut<State<MyState>>| {
r.0.push("update S5");
s.overwrite_push(MyState::S6).unwrap();
})
.after("inactive s4"),
@ -556,15 +561,15 @@ mod test {
)
.add_system_set(
State::on_inactive_update_set(MyState::S5).with_system(
(|mut r: ResMut<Vec<&'static str>>| r.push("inactive S5"))
(|mut r: ResMut<NameList>| r.0.push("inactive S5"))
.label("inactive s5")
.after("inactive s4"),
),
)
.add_system_set(
State::on_update_set(MyState::S6).with_system(
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S6");
(|mut r: ResMut<NameList>, mut s: ResMut<State<MyState>>| {
r.0.push("update S6");
s.overwrite_push(MyState::Final).unwrap();
})
.after("inactive s5"),
@ -572,11 +577,11 @@ mod test {
)
.add_system_set(
State::on_resume_set(MyState::S4)
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("resume S4")),
.with_system(|mut r: ResMut<NameList>| r.0.push("resume S4")),
)
.add_system_set(
State::on_exit_set(MyState::S5)
.with_system(|mut r: ResMut<Vec<&'static str>>| r.push("exit S4")),
.with_system(|mut r: ResMut<NameList>| r.0.push("exit S4")),
);
const EXPECTED: &[&str] = &[
@ -606,9 +611,9 @@ mod test {
];
stage.run(&mut world);
let mut collected = world.resource_mut::<Vec<&'static str>>();
let mut collected = world.resource_mut::<NameList>();
let mut count = 0;
for (found, expected) in collected.drain(..).zip(EXPECTED) {
for (found, expected) in collected.0.drain(..).zip(EXPECTED) {
assert_eq!(found, *expected);
count += 1;
}
@ -627,26 +632,32 @@ mod test {
Main,
}
fn should_run_once(mut flag: ResMut<bool>, test_name: Res<&'static str>) {
assert!(!*flag, "{:?}", *test_name);
*flag = true;
#[derive(Resource)]
struct Flag(bool);
#[derive(Resource)]
struct Name(&'static str);
fn should_run_once(mut flag: ResMut<Flag>, test_name: Res<Name>) {
assert!(!flag.0, "{:?}", test_name.0);
flag.0 = true;
}
let mut world = World::new();
world.insert_resource(State::new(AppState::Main));
world.insert_resource(false);
world.insert_resource("control");
world.insert_resource(Flag(false));
world.insert_resource(Name("control"));
let mut stage = SystemStage::parallel().with_system(should_run_once);
stage.run(&mut world);
assert!(*world.resource::<bool>(), "after control");
assert!(world.resource::<Flag>().0, "after control");
world.insert_resource(false);
world.insert_resource("test");
world.insert_resource(Flag(false));
world.insert_resource(Name("test"));
let mut stage = SystemStage::parallel()
.with_system_set(State::<AppState>::get_driver())
.with_system(should_run_once);
stage.run(&mut world);
assert!(*world.resource::<bool>(), "after test");
assert!(world.resource::<Flag>().0, "after test");
}
#[test]
@ -664,8 +675,11 @@ mod test {
EnterFinish,
}
#[derive(Resource, Default)]
struct LoadStatusStack(Vec<LoadStatus>);
let mut world = World::new();
world.insert_resource(Vec::<LoadStatus>::new());
world.init_resource::<LoadStatusStack>();
world.insert_resource(State::new(LoadState::Load));
let mut stage = SystemStage::parallel();
@ -675,15 +689,16 @@ mod test {
stage
.add_system_set(
State::on_enter_set(LoadState::Load)
.with_system(|mut r: ResMut<Vec<LoadStatus>>| r.push(LoadStatus::EnterLoad)),
.with_system(|mut r: ResMut<LoadStatusStack>| r.0.push(LoadStatus::EnterLoad)),
)
.add_system_set(
State::on_exit_set(LoadState::Load)
.with_system(|mut r: ResMut<Vec<LoadStatus>>| r.push(LoadStatus::ExitLoad)),
.with_system(|mut r: ResMut<LoadStatusStack>| r.0.push(LoadStatus::ExitLoad)),
)
.add_system_set(
State::on_enter_set(LoadState::Finish)
.with_system(|mut r: ResMut<Vec<LoadStatus>>| r.push(LoadStatus::EnterFinish)),
State::on_enter_set(LoadState::Finish).with_system(
|mut r: ResMut<LoadStatusStack>| r.0.push(LoadStatus::EnterFinish),
),
);
stage.run(&mut world);
@ -720,9 +735,9 @@ mod test {
LoadStatus::EnterFinish,
];
let mut collected = world.resource_mut::<Vec<LoadStatus>>();
let mut collected = world.resource_mut::<LoadStatusStack>();
let mut count = 0;
for (found, expected) in collected.drain(..).zip(EXPECTED) {
for (found, expected) in collected.0.drain(..).zip(EXPECTED) {
assert_eq!(found, *expected);
count += 1;
}

View file

@ -24,7 +24,7 @@ use super::Resource;
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::Command;
/// // Our world resource
/// #[derive(Default)]
/// #[derive(Resource, Default)]
/// struct Counter(u64);
///
/// // Our custom command
@ -331,7 +331,7 @@ impl<'w, 's> Commands<'w, 's> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Default)]
/// # #[derive(Resource, Default)]
/// # struct Scoreboard {
/// # current_score: u32,
/// # high_score: u32,
@ -359,6 +359,7 @@ impl<'w, 's> Commands<'w, 's> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Resource)]
/// # struct Scoreboard {
/// # current_score: u32,
/// # high_score: u32,
@ -385,6 +386,7 @@ impl<'w, 's> Commands<'w, 's> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Resource)]
/// # struct Scoreboard {
/// # current_score: u32,
/// # high_score: u32,
@ -410,7 +412,7 @@ impl<'w, 's> Commands<'w, 's> {
///
/// ```
/// # use bevy_ecs::{system::Command, prelude::*};
/// #[derive(Default)]
/// #[derive(Resource, Default)]
/// struct Counter(u64);
///
/// struct AddToCounter(u64);
@ -472,6 +474,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Resource)]
/// # struct PlayerEntity { entity: Entity }
/// # #[derive(Component)]
/// # struct Health(u32);
@ -548,6 +551,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Resource)]
/// # struct PlayerEntity { entity: Entity }
/// #
/// # #[derive(Component)]
@ -580,6 +584,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Resource)]
/// # struct TargetEnemy { entity: Entity }
/// # #[derive(Component)]
/// # struct Enemy;
@ -609,6 +614,7 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Resource)]
/// # struct CharacterToRemove { entity: Entity }
/// #
/// fn remove_character_system(
@ -854,7 +860,7 @@ mod tests {
use crate::{
self as bevy_ecs,
component::Component,
system::{CommandQueue, Commands},
system::{CommandQueue, Commands, Resource},
world::World,
};
use std::sync::{
@ -881,7 +887,7 @@ mod tests {
}
}
#[derive(Component)]
#[derive(Component, Resource)]
struct W<T>(T);
fn simple_command(world: &mut World) {
@ -992,21 +998,21 @@ mod tests {
let mut queue = CommandQueue::default();
{
let mut commands = Commands::new(&mut queue, &world);
commands.insert_resource(123);
commands.insert_resource(456.0);
commands.insert_resource(W(123i32));
commands.insert_resource(W(456.0f64));
}
queue.apply(&mut world);
assert!(world.contains_resource::<i32>());
assert!(world.contains_resource::<f64>());
assert!(world.contains_resource::<W<i32>>());
assert!(world.contains_resource::<W<f64>>());
{
let mut commands = Commands::new(&mut queue, &world);
// test resource removal
commands.remove_resource::<i32>();
commands.remove_resource::<W<i32>>();
}
queue.apply(&mut world);
assert!(!world.contains_resource::<i32>());
assert!(world.contains_resource::<f64>());
assert!(!world.contains_resource::<W<i32>>());
assert!(world.contains_resource::<W<f64>>());
}
}

View file

@ -112,7 +112,7 @@ mod tests {
entity::Entity,
query::With,
schedule::{Stage, SystemStage},
system::{Commands, IntoExclusiveSystem, Query, ResMut},
system::{Commands, IntoExclusiveSystem, Query, ResMut, Resource},
world::World,
};
@ -123,49 +123,55 @@ mod tests {
fn parallel_with_commands_as_exclusive() {
let mut world = World::new();
#[derive(Resource)]
struct Counter(usize);
fn removal(
mut commands: Commands,
query: Query<Entity, With<Foo>>,
mut counter: ResMut<usize>,
mut counter: ResMut<Counter>,
) {
for entity in &query {
*counter += 1;
counter.0 += 1;
commands.entity(entity).remove::<Foo>();
}
}
let mut stage = SystemStage::parallel().with_system(removal);
world.spawn().insert(Foo(0.0f32));
world.insert_resource(0usize);
world.insert_resource(Counter(0));
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(*world.resource::<usize>(), 1);
assert_eq!(world.resource::<Counter>().0, 1);
let mut stage = SystemStage::parallel().with_system(removal.exclusive_system());
world.spawn().insert(Foo(0.0f32));
world.insert_resource(0usize);
world.insert_resource(Counter(0));
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(*world.resource::<usize>(), 1);
assert_eq!(world.resource::<Counter>().0, 1);
}
#[test]
fn update_archetype_for_exclusive_system_coerced() {
#[derive(Resource, Default)]
struct CountEntities(Vec<usize>);
fn spawn_entity(mut commands: crate::prelude::Commands) {
commands.spawn().insert(Foo(0.0));
}
fn count_entities(query: Query<&Foo>, mut res: ResMut<Vec<usize>>) {
res.push(query.iter().len());
fn count_entities(query: Query<&Foo>, mut res: ResMut<CountEntities>) {
res.0.push(query.iter().len());
}
let mut world = World::new();
world.insert_resource(Vec::<usize>::new());
world.init_resource::<CountEntities>();
let mut stage = SystemStage::parallel()
.with_system(spawn_entity)
.with_system(count_entities.exclusive_system());
stage.run(&mut world);
stage.run(&mut world);
assert_eq!(*world.resource::<Vec<usize>>(), vec![0, 1]);
assert_eq!(world.resource::<CountEntities>().0, vec![0, 1]);
}
}

View file

@ -82,6 +82,7 @@ impl SystemMeta {
/// use bevy_ecs::event::Events;
///
/// struct MyEvent;
/// #[derive(Resource)]
/// struct MyResource(u32);
///
/// #[derive(Component)]
@ -110,6 +111,7 @@ impl SystemMeta {
/// use bevy_ecs::event::Events;
///
/// struct MyEvent;
/// #[derive(Resource)]
/// struct CachedSystemState<'w, 's>{
/// event_state: SystemState<EventReader<'w, 's, MyEvent>>
/// }
@ -246,10 +248,9 @@ impl<Param: SystemParam> FromWorld for SystemState<Param> {
/// # Examples
///
/// ```
/// use bevy_ecs::system::IntoSystem;
/// use bevy_ecs::system::Res;
/// use bevy_ecs::prelude::*;
///
/// fn my_system_function(an_usize_resource: Res<usize>) {}
/// fn my_system_function(a_usize_local: Local<usize>) {}
///
/// let system = IntoSystem::into_system(my_system_function);
/// ```
@ -526,6 +527,7 @@ impl<T> Copy for SystemTypeIdLabel<T> {}
/// assert_eq!(chained_system.run((), &mut world), Some(42));
/// }
///
/// #[derive(Resource)]
/// struct Message(String);
///
/// fn parse_message(message: Res<Message>) -> Result<usize, ParseIntError> {

View file

@ -17,6 +17,7 @@
//! # struct Player { alive: bool }
//! # #[derive(Component)]
//! # struct Score(u32);
//! # #[derive(Resource)]
//! # struct Round(u32);
//! #
//! fn update_score_system(
@ -139,22 +140,28 @@ mod tests {
schedule::{Schedule, Stage, SystemStage},
system::{
Commands, IntoExclusiveSystem, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query,
RemovedComponents, Res, ResMut, System, SystemState,
RemovedComponents, Res, ResMut, Resource, System, SystemState,
},
world::{FromWorld, World},
};
#[derive(Component, Debug, Eq, PartialEq, Default)]
#[derive(Resource, PartialEq, Debug)]
enum SystemRan {
Yes,
No,
}
#[derive(Component, Resource, Debug, Eq, PartialEq, Default)]
struct A;
#[derive(Component)]
#[derive(Component, Resource)]
struct B;
#[derive(Component)]
#[derive(Component, Resource)]
struct C;
#[derive(Component)]
#[derive(Component, Resource)]
struct D;
#[derive(Component)]
#[derive(Component, Resource)]
struct E;
#[derive(Component)]
#[derive(Component, Resource)]
struct F;
#[derive(Component)]
@ -187,7 +194,7 @@ mod tests {
#[test]
fn query_system_gets() {
fn query_system(
mut ran: ResMut<bool>,
mut ran: ResMut<SystemRan>,
entity_query: Query<Entity, With<A>>,
b_query: Query<&B>,
a_c_query: Query<(&A, &C)>,
@ -227,11 +234,11 @@ mod tests {
"entity 3 should have D"
);
*ran = true;
*ran = SystemRan::Yes;
}
let mut world = World::default();
world.insert_resource(false);
world.insert_resource(SystemRan::No);
world.spawn().insert_bundle((A,));
world.spawn().insert_bundle((A, B));
world.spawn().insert_bundle((A, C));
@ -239,14 +246,14 @@ mod tests {
run_system(&mut world, query_system);
assert!(*world.resource::<bool>(), "system ran");
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
}
#[test]
fn or_param_set_system() {
// Regression test for issue #762
fn query_system(
mut ran: ResMut<bool>,
mut ran: ResMut<SystemRan>,
mut set: ParamSet<(
Query<(), Or<(Changed<A>, Changed<B>)>>,
Query<(), Or<(Added<A>, Added<B>)>>,
@ -258,24 +265,33 @@ mod tests {
assert_eq!(changed, 1);
assert_eq!(added, 1);
*ran = true;
*ran = SystemRan::Yes;
}
let mut world = World::default();
world.insert_resource(false);
world.insert_resource(SystemRan::No);
world.spawn().insert_bundle((A, B));
run_system(&mut world, query_system);
assert!(*world.resource::<bool>(), "system ran");
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
}
#[test]
fn changed_resource_system() {
use crate::system::Resource;
#[derive(Resource)]
struct Flipper(bool);
#[derive(Resource)]
struct Added(usize);
#[derive(Resource)]
struct Changed(usize);
fn incr_e_on_flip(
value: Res<bool>,
value: Res<Flipper>,
mut changed: ResMut<Changed>,
mut added: ResMut<Added>,
) {
@ -289,7 +305,7 @@ mod tests {
}
let mut world = World::default();
world.insert_resource(false);
world.insert_resource(Flipper(false));
world.insert_resource(Added(0));
world.insert_resource(Changed(0));
@ -310,7 +326,7 @@ mod tests {
assert_eq!(world.resource::<Added>().0, 1);
assert_eq!(world.resource::<Changed>().0, 1);
*world.resource_mut::<bool>() = true;
world.resource_mut::<Flipper>().0 = true;
schedule.run(&mut world);
assert_eq!(world.resource::<Added>().0, 1);
assert_eq!(world.resource::<Changed>().0, 2);
@ -434,7 +450,7 @@ mod tests {
run_system(&mut world, sys);
}
#[derive(Default)]
#[derive(Default, Resource)]
struct BufferRes {
_buffer: Vec<u8>,
}
@ -477,36 +493,42 @@ mod tests {
#[test]
fn local_system() {
let mut world = World::default();
world.insert_resource(1u32);
world.insert_resource(false);
world.insert_resource(ProtoFoo { value: 1 });
world.insert_resource(SystemRan::No);
struct Foo {
value: u32,
}
#[derive(Resource)]
struct ProtoFoo {
value: u32,
}
impl FromWorld for Foo {
fn from_world(world: &mut World) -> Self {
Foo {
value: *world.resource::<u32>() + 1,
value: world.resource::<ProtoFoo>().value + 1,
}
}
}
fn sys(local: Local<Foo>, mut modified: ResMut<bool>) {
fn sys(local: Local<Foo>, mut system_ran: ResMut<SystemRan>) {
assert_eq!(local.value, 2);
*modified = true;
*system_ran = SystemRan::Yes;
}
run_system(&mut world, sys);
// ensure the system actually ran
assert!(*world.resource::<bool>());
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
}
#[test]
fn non_send_option_system() {
let mut world = World::default();
world.insert_resource(false);
world.insert_resource(SystemRan::No);
struct NotSend1(std::rc::Rc<i32>);
struct NotSend2(std::rc::Rc<i32>);
world.insert_non_send_resource(NotSend1(std::rc::Rc::new(0)));
@ -514,34 +536,38 @@ mod tests {
fn sys(
op: Option<NonSend<NotSend1>>,
mut _op2: Option<NonSendMut<NotSend2>>,
mut run: ResMut<bool>,
mut system_ran: ResMut<SystemRan>,
) {
op.expect("NonSend should exist");
*run = true;
*system_ran = SystemRan::Yes;
}
run_system(&mut world, sys);
// ensure the system actually ran
assert!(*world.resource::<bool>());
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
}
#[test]
fn non_send_system() {
let mut world = World::default();
world.insert_resource(false);
world.insert_resource(SystemRan::No);
struct NotSend1(std::rc::Rc<i32>);
struct NotSend2(std::rc::Rc<i32>);
world.insert_non_send_resource(NotSend1(std::rc::Rc::new(1)));
world.insert_non_send_resource(NotSend2(std::rc::Rc::new(2)));
fn sys(_op: NonSend<NotSend1>, mut _op2: NonSendMut<NotSend2>, mut run: ResMut<bool>) {
*run = true;
fn sys(
_op: NonSend<NotSend1>,
mut _op2: NonSendMut<NotSend2>,
mut system_ran: ResMut<SystemRan>,
) {
*system_ran = SystemRan::Yes;
}
run_system(&mut world, sys);
assert!(*world.resource::<bool>());
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
}
#[test]
@ -553,13 +579,16 @@ mod tests {
let spurious_entity = world.spawn().id();
// Track which entities we want to operate on
#[derive(Resource)]
struct Despawned(Entity);
world.insert_resource(Despawned(entity_to_despawn));
#[derive(Resource)]
struct Removed(Entity);
world.insert_resource(Removed(entity_to_remove_w_from));
// Verify that all the systems actually ran
#[derive(Default)]
#[derive(Default, Resource)]
struct NSystems(usize);
world.insert_resource(NSystems::default());
@ -615,7 +644,7 @@ mod tests {
#[test]
fn world_collections_system() {
let mut world = World::default();
world.insert_resource(false);
world.insert_resource(SystemRan::No);
world.spawn().insert_bundle((W(42), W(true)));
fn sys(
archetypes: &Archetypes,
@ -623,7 +652,7 @@ mod tests {
entities: &Entities,
bundles: &Bundles,
query: Query<Entity, With<W<i32>>>,
mut modified: ResMut<bool>,
mut system_ran: ResMut<SystemRan>,
) {
assert_eq!(query.iter().count(), 1, "entity exists");
for entity in &query {
@ -647,13 +676,13 @@ mod tests {
"entity's bundle components exactly match entity's archetype components"
);
}
*modified = true;
*system_ran = SystemRan::Yes;
}
run_system(&mut world, sys);
// ensure the system actually ran
assert!(*world.resource::<bool>());
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
}
#[test]
@ -751,7 +780,7 @@ mod tests {
#[test]
fn read_system_state() {
#[derive(Eq, PartialEq, Debug)]
#[derive(Eq, PartialEq, Debug, Resource)]
struct A(usize);
#[derive(Component, Eq, PartialEq, Debug)]
@ -774,7 +803,7 @@ mod tests {
#[test]
fn write_system_state() {
#[derive(Eq, PartialEq, Debug)]
#[derive(Resource, Eq, PartialEq, Debug)]
struct A(usize);
#[derive(Component, Eq, PartialEq, Debug)]

View file

@ -735,6 +735,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Resource)]
/// # struct SelectedCharacter { entity: Entity }
/// # #[derive(Component)]
/// # struct Character { name: String }
@ -841,6 +842,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Resource)]
/// # struct PoisonedCharacter { character_id: Entity }
/// # #[derive(Component)]
/// # struct Health(u32);
@ -967,6 +969,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Resource)]
/// # struct SelectedCharacter { entity: Entity }
/// # #[derive(Component)]
/// # struct Character { name: String }
@ -1022,6 +1025,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Resource)]
/// # struct PoisonedCharacter { character_id: Entity }
/// # #[derive(Component)]
/// # struct Health(u32);
@ -1233,7 +1237,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
/// #
/// # #[derive(Component)]
/// # struct Player;
/// # #[derive(Component)]
/// # #[derive(Resource)]
/// # struct Score(u32);
/// fn update_score_system(query: Query<(), With<Player>>, mut score: ResMut<Score>) {
/// if !query.is_empty() {
@ -1258,6 +1262,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
/// # #[derive(Component)]
/// # struct InRange;
/// #
/// # #[derive(Resource)]
/// # struct Target {
/// # entity: Entity,
/// # }
@ -1352,6 +1357,7 @@ impl<'w, 's, Q: ReadOnlyWorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Resource)]
/// # struct SelectedCharacter { entity: Entity }
/// # #[derive(Component)]
/// # struct Character { name: String }

View file

@ -34,6 +34,7 @@ use std::borrow::Cow;
/// assert_eq!(chained_system.run((), &mut world), Some(42));
/// }
///
/// #[derive(Resource)]
/// struct Message(String);
///
/// fn parse_message_system(message: Res<Message>) -> Result<usize, ParseIntError> {

View file

@ -11,6 +11,7 @@ use crate::{
system::{CommandQueue, Commands, Query, SystemMeta},
world::{FromWorld, World},
};
pub use bevy_ecs_macros::Resource;
pub use bevy_ecs_macros::SystemParam;
use bevy_ecs_macros::{all_tuples, impl_param_set};
use bevy_ptr::UnsafeCellDeref;
@ -48,14 +49,16 @@ use std::{
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # #[derive(Resource)]
/// # struct SomeResource;
/// use std::marker::PhantomData;
/// use bevy_ecs::system::SystemParam;
///
/// #[derive(SystemParam)]
/// struct MyParam<'w, 's> {
/// foo: Res<'w, usize>,
/// foo: Res<'w, SomeResource>,
/// #[system_param(ignore)]
/// marker: PhantomData<&'s usize>,
/// marker: PhantomData<&'s ()>,
/// }
///
/// fn my_system(param: MyParam) {
@ -217,13 +220,42 @@ pub struct ParamSetState<T: for<'w, 's> SystemParamFetch<'w, 's>>(T);
impl_param_set!();
/// A type that can be inserted into a [`World`] as a singleton.
///
/// You can access resource data in systems using the [`Res`] and [`ResMut`] system parameters
///
/// Only one resource of each type can be stored in a [`World`] at any given time.
///
/// # Examples
///
/// ```
/// # let mut world = World::default();
/// # let mut schedule = Schedule::default();
/// # schedule.add_stage("update", SystemStage::parallel());
/// # use bevy_ecs::prelude::*;
/// #[derive(Resource)]
/// struct MyResource { value: u32 }
///
/// world.insert_resource(MyResource { value: 42 });
///
/// fn read_resource_system(resource: Res<MyResource>) {
/// assert_eq!(resource.value, 42);
/// }
///
/// fn write_resource_system(mut resource: ResMut<MyResource>) {
/// assert_eq!(resource.value, 42);
/// resource.value = 0;
/// assert_eq!(resource.value, 0);
/// }
/// # schedule.add_system_to_stage("update", read_resource_system.label("first"));
/// # schedule.add_system_to_stage("update", write_resource_system.after("first"));
/// # schedule.run_once(&mut world);
/// ```
pub trait Resource: Send + Sync + 'static {}
impl<T> Resource for T where T: Send + Sync + 'static {}
/// Shared borrow of a resource.
/// Shared borrow of a [`Resource`].
///
/// See the [`World`] documentation to see the usage of a resource.
/// See the [`Resource`] documentation for usage.
///
/// If you need a unique mutable borrow, use [`ResMut`] instead.
///
@ -632,6 +664,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for WorldState {
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::assert_is_system;
/// struct Config(u32);
/// #[derive(Resource)]
/// struct Myu32Wrapper(u32);
/// fn reset_to_system(value: Config) -> impl FnMut(ResMut<Myu32Wrapper>) {
/// move |mut val| val.0 = value.0
@ -640,12 +673,12 @@ impl<'w, 's> SystemParamFetch<'w, 's> for WorldState {
/// // .add_system(reset_to_system(my_config))
/// # assert_is_system(reset_to_system(Config(10)));
/// ```
pub struct Local<'a, T: Resource + FromWorld>(&'a mut T);
pub struct Local<'a, T: FromWorld + Send + Sync + 'static>(&'a mut T);
// SAFETY: Local only accesses internal state
unsafe impl<T: Resource> ReadOnlySystemParamFetch for LocalState<T> {}
// SAFE: Local only accesses internal state
unsafe impl<T: Send + Sync + 'static> ReadOnlySystemParamFetch for LocalState<T> {}
impl<'a, T: Resource + FromWorld> Debug for Local<'a, T>
impl<'a, T: FromWorld + Send + Sync + 'static> Debug for Local<'a, T>
where
T: Debug,
{
@ -654,7 +687,7 @@ where
}
}
impl<'a, T: Resource + FromWorld> Deref for Local<'a, T> {
impl<'a, T: FromWorld + Send + Sync + 'static> Deref for Local<'a, T> {
type Target = T;
#[inline]
@ -663,7 +696,7 @@ impl<'a, T: Resource + FromWorld> Deref for Local<'a, T> {
}
}
impl<'a, T: Resource + FromWorld> DerefMut for Local<'a, T> {
impl<'a, T: FromWorld + Send + Sync + 'static> DerefMut for Local<'a, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
@ -672,20 +705,20 @@ impl<'a, T: Resource + FromWorld> DerefMut for Local<'a, T> {
/// The [`SystemParamState`] of [`Local<T>`].
#[doc(hidden)]
pub struct LocalState<T: Resource>(T);
pub struct LocalState<T: Send + Sync + 'static>(T);
impl<'a, T: Resource + FromWorld> SystemParam for Local<'a, T> {
impl<'a, T: Send + Sync + 'static + FromWorld> SystemParam for Local<'a, T> {
type Fetch = LocalState<T>;
}
// SAFETY: only local state is accessed
unsafe impl<T: Resource + FromWorld> SystemParamState for LocalState<T> {
// SAFE: only local state is accessed
unsafe impl<T: FromWorld + Send + Sync + 'static> SystemParamState for LocalState<T> {
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self {
Self(T::from_world(world))
}
}
impl<'w, 's, T: Resource + FromWorld> SystemParamFetch<'w, 's> for LocalState<T> {
impl<'w, 's, T: Send + Sync + 'static + FromWorld> SystemParamFetch<'w, 's> for LocalState<T> {
type Item = Local<'s, T>;
#[inline]

View file

@ -30,6 +30,7 @@ use std::{
mod identifier;
pub use identifier::WorldId;
/// Stores and exposes operations on [entities](Entity), [components](Component), resources,
/// and their associated metadata.
///
@ -43,41 +44,12 @@ pub use identifier::WorldId;
/// To mutate different parts of the world simultaneously,
/// use [`World::resource_scope`] or [`SystemState`](crate::system::SystemState).
///
/// # Resources
/// ## Resources
///
/// Worlds can also store *resources*, which are unique instances of a given type that don't
/// belong to a specific Entity. There are also *non send resources*, which can only be
/// accessed on the main thread.
///
/// ## Usage of global resources
///
/// 1. Insert the resource into the `World`, using [`World::insert_resource`].
/// 2. Fetch the resource from a system, using [`Res`](crate::system::Res) or [`ResMut`](crate::system::ResMut).
///
/// ```
/// # let mut world = World::default();
/// # let mut schedule = Schedule::default();
/// # schedule.add_stage("update", SystemStage::parallel());
/// # use bevy_ecs::prelude::*;
/// #
/// struct MyResource { value: u32 }
///
/// world.insert_resource(MyResource { value: 42 });
///
/// fn read_resource_system(resource: Res<MyResource>) {
/// assert_eq!(resource.value, 42);
/// }
///
/// fn write_resource_system(mut resource: ResMut<MyResource>) {
/// assert_eq!(resource.value, 42);
/// resource.value = 0;
/// assert_eq!(resource.value, 0);
/// }
/// #
/// # schedule.add_system_to_stage("update", read_resource_system.label("first"));
/// # schedule.add_system_to_stage("update", write_resource_system.after("first"));
/// # schedule.run_once(&mut world);
/// ```
/// Worlds can also store [`Resource`]s,
/// which are unique instances of a given type that don't belong to a specific Entity.
/// There are also *non send resources*, which can only be accessed on the main thread.
/// See [`Resource`] for usage.
pub struct World {
id: WorldId,
pub(crate) entities: Entities,
@ -1089,8 +1061,8 @@ impl World {
///
/// # Example
/// ```
/// use bevy_ecs::{component::Component, world::{World, Mut}};
/// #[derive(Component)]
/// use bevy_ecs::prelude::*;
/// #[derive(Resource)]
/// struct A(u32);
/// #[derive(Component)]
/// struct B(u32);
@ -1597,6 +1569,7 @@ mod tests {
change_detection::DetectChanges,
component::{ComponentDescriptor, ComponentId, ComponentInfo, StorageType},
ptr::OwningPtr,
system::Resource,
};
use bevy_ecs_macros::Component;
use bevy_utils::HashSet;
@ -1726,7 +1699,7 @@ mod tests {
);
}
#[derive(Component)]
#[derive(Resource)]
struct TestResource(u32);
#[test]

View file

@ -318,40 +318,47 @@ impl<'w> WorldCell<'w> {
#[cfg(test)]
mod tests {
use super::BASE_ACCESS;
use crate::{archetype::ArchetypeId, world::World};
use crate as bevy_ecs;
use crate::{archetype::ArchetypeId, system::Resource, world::World};
use std::any::TypeId;
#[derive(Resource)]
struct A(u32);
#[derive(Resource)]
struct B(u64);
#[test]
fn world_cell() {
let mut world = World::default();
world.insert_resource(1u32);
world.insert_resource(1u64);
world.insert_resource(A(1));
world.insert_resource(B(1));
let cell = world.cell();
{
let mut a = cell.resource_mut::<u32>();
assert_eq!(1, *a);
*a = 2;
let mut a = cell.resource_mut::<A>();
assert_eq!(1, a.0);
a.0 = 2;
}
{
let a = cell.resource::<u32>();
assert_eq!(2, *a, "ensure access is dropped");
let a = cell.resource::<A>();
assert_eq!(2, a.0, "ensure access is dropped");
let b = cell.resource::<u32>();
let a2 = cell.resource::<A>();
assert_eq!(
2, *b,
2, a2.0,
"ensure multiple immutable accesses can occur at the same time"
);
}
{
let a = cell.resource_mut::<u32>();
let a = cell.resource_mut::<A>();
assert_eq!(
2, *a,
2, a.0,
"ensure both immutable accesses are dropped, enabling a new mutable access"
);
let b = cell.resource::<u64>();
let b = cell.resource::<B>();
assert_eq!(
1, *b,
1, b.0,
"ensure multiple non-conflicting mutable accesses can occur at the same time"
);
}
@ -360,23 +367,20 @@ mod tests {
#[test]
fn world_access_reused() {
let mut world = World::default();
world.insert_resource(1u32);
world.insert_resource(A(1));
{
let cell = world.cell();
{
let mut a = cell.resource_mut::<u32>();
assert_eq!(1, *a);
*a = 2;
let mut a = cell.resource_mut::<A>();
assert_eq!(1, a.0);
a.0 = 2;
}
}
let u32_component_id = world
.components
.get_resource_id(TypeId::of::<u32>())
.unwrap();
let resource_id = world.components.get_resource_id(TypeId::of::<A>()).unwrap();
let resource_archetype = world.archetypes.get(ArchetypeId::RESOURCE).unwrap();
let u32_archetype_component_id = resource_archetype
.get_archetype_component_id(u32_component_id)
.get_archetype_component_id(resource_id)
.unwrap();
assert_eq!(world.archetype_component_access.access.len(), 1);
assert_eq!(
@ -393,38 +397,38 @@ mod tests {
#[should_panic]
fn world_cell_double_mut() {
let mut world = World::default();
world.insert_resource(1u32);
world.insert_resource(A(1));
let cell = world.cell();
let _value_a = cell.resource_mut::<u32>();
let _value_b = cell.resource_mut::<u32>();
let _value_a = cell.resource_mut::<A>();
let _value_b = cell.resource_mut::<A>();
}
#[test]
#[should_panic]
fn world_cell_ref_and_mut() {
let mut world = World::default();
world.insert_resource(1u32);
world.insert_resource(A(1));
let cell = world.cell();
let _value_a = cell.resource::<u32>();
let _value_b = cell.resource_mut::<u32>();
let _value_a = cell.resource::<A>();
let _value_b = cell.resource_mut::<A>();
}
#[test]
#[should_panic]
fn world_cell_mut_and_ref() {
let mut world = World::default();
world.insert_resource(1u32);
world.insert_resource(A(1));
let cell = world.cell();
let _value_a = cell.resource_mut::<u32>();
let _value_b = cell.resource::<u32>();
let _value_a = cell.resource_mut::<A>();
let _value_b = cell.resource::<A>();
}
#[test]
fn world_cell_ref_and_ref() {
let mut world = World::default();
world.insert_resource(1u32);
world.insert_resource(A(1));
let cell = world.cell();
let _value_a = cell.resource::<u32>();
let _value_b = cell.resource::<u32>();
let _value_a = cell.resource::<A>();
let _value_b = cell.resource::<A>();
}
}

View file

@ -3,8 +3,8 @@ use bevy_ecs::prelude::*;
#[derive(Component)]
struct A(usize);
fn system(mut query: Query<&mut A>, e: Res<Entity>) {
let iter = query.iter_many_mut([*e]);
fn system(mut query: Query<&mut A>, e: Entity) {
let iter = query.iter_many_mut([e]);
// This should fail to compile.
is_iterator(iter)

View file

@ -3,9 +3,9 @@ use bevy_ecs::prelude::*;
#[derive(Component)]
struct A(usize);
fn system(mut query: Query<&mut A>, e: Res<Entity>) {
let a1 = query.get_mut(*e).unwrap();
let a2 = query.get_mut(*e).unwrap();
fn system(mut query: Query<&mut A>, e: Entity) {
let a1 = query.get_mut(e).unwrap();
let a2 = query.get_mut(e).unwrap();
// this should fail to compile
println!("{} {}", a1.0, a2.0);
}

View file

@ -1,10 +1,10 @@
error[E0499]: cannot borrow `query` as mutable more than once at a time
--> tests/ui/system_query_get_lifetime_safety.rs:8:14
|
7 | let a1 = query.get_mut(*e).unwrap();
| ----------------- first mutable borrow occurs here
8 | let a2 = query.get_mut(*e).unwrap();
| ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
7 | let a1 = query.get_mut(e).unwrap();
| ---------------- first mutable borrow occurs here
8 | let a2 = query.get_mut(e).unwrap();
| ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
9 | // this should fail to compile
10 | println!("{} {}", a1.0, a2.0);
| -- first borrow later used here

View file

@ -3,9 +3,9 @@ use bevy_ecs::prelude::*;
#[derive(Component)]
struct A(usize);
fn system(mut query: Query<&mut A>, e: Res<Entity>) {
let a1 = query.get_many([*e, *e]).unwrap();
let a2 = query.get_mut(*e).unwrap();
fn system(mut query: Query<&mut A>, e: Entity) {
let a1 = query.get_many([e, e]).unwrap();
let a2 = query.get_mut(e).unwrap();
// this should fail to compile
println!("{} {}", a1[0].0, a2.0);
}

View file

@ -1,10 +1,10 @@
error[E0502]: cannot borrow `query` as mutable because it is also borrowed as immutable
--> tests/ui/system_query_get_many_lifetime_safety.rs:8:14
|
7 | let a1 = query.get_many([*e, *e]).unwrap();
| ------------------------ immutable borrow occurs here
8 | let a2 = query.get_mut(*e).unwrap();
| ^^^^^^^^^^^^^^^^^ mutable borrow occurs here
7 | let a1 = query.get_many([e, e]).unwrap();
| ---------------------- immutable borrow occurs here
8 | let a2 = query.get_mut(e).unwrap();
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
9 | // this should fail to compile
10 | println!("{} {}", a1[0].0, a2.0);
| ----- immutable borrow later used here

View file

@ -3,9 +3,9 @@ use bevy_ecs::prelude::*;
#[derive(Component)]
struct A(usize);
fn system(mut query: Query<&mut A>, e: Res<Entity>) {
let a1 = query.get_many_mut([*e, *e]).unwrap();
let a2 = query.get_mut(*e).unwrap();
fn system(mut query: Query<&mut A>, e: Entity) {
let a1 = query.get_many_mut([e, e]).unwrap();
let a2 = query.get_mut(e).unwrap();
// this should fail to compile
println!("{} {}", a1[0].0, a2.0);
}

View file

@ -1,10 +1,10 @@
error[E0499]: cannot borrow `query` as mutable more than once at a time
--> tests/ui/system_query_get_many_mut_lifetime_safety.rs:8:14
|
7 | let a1 = query.get_many_mut([*e, *e]).unwrap();
| ---------------------------- first mutable borrow occurs here
8 | let a2 = query.get_mut(*e).unwrap();
| ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
7 | let a1 = query.get_many_mut([e, e]).unwrap();
| -------------------------- first mutable borrow occurs here
8 | let a2 = query.get_mut(e).unwrap();
| ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
9 | // this should fail to compile
10 | println!("{} {}", a1[0].0, a2.0);
| ----- first borrow later used here

View file

@ -3,9 +3,9 @@ use bevy_ecs::prelude::*;
#[derive(Component)]
struct A(usize);
fn system(mut query: Query<&mut A>, e: Res<Entity>) {
fn system(mut query: Query<&mut A>, e: Entity) {
let mut results = Vec::new();
let mut iter = query.iter_many_mut([*e, *e]);
let mut iter = query.iter_many_mut([e, e]);
while let Some(a) = iter.fetch_next() {
// this should fail to compile
results.push(a);

View file

@ -3,23 +3,23 @@ use bevy_ecs::prelude::*;
#[derive(Component)]
struct A(usize);
fn query_set(mut queries: ParamSet<(Query<&mut A>, Query<&A>)>, e: Res<Entity>) {
fn query_set(mut queries: ParamSet<(Query<&mut A>, Query<&A>)>, e: Entity) {
let mut q2 = queries.p0();
let mut b = q2.get_mut(*e).unwrap();
let mut b = q2.get_mut(e).unwrap();
let q1 = queries.p1();
let a = q1.get(*e).unwrap();
let a = q1.get(e).unwrap();
// this should fail to compile
b.0 = a.0
}
fn query_set_flip(mut queries: ParamSet<(Query<&mut A>, Query<&A>)>, e: Res<Entity>) {
fn query_set_flip(mut queries: ParamSet<(Query<&mut A>, Query<&A>)>, e: Entity) {
let q1 = queries.p1();
let a = q1.get(*e).unwrap();
let a = q1.get(e).unwrap();
let mut q2 = queries.p0();
let mut b = q2.get_mut(*e).unwrap();
let mut b = q2.get_mut(e).unwrap();
// this should fail to compile
b.0 = a.0

View file

@ -1,10 +1,11 @@
use bevy_ecs::system::Resource;
use bevy_utils::HashMap;
use std::hash::Hash;
/// Stores the position data of the input devices of type `T`.
///
/// The values are stored as `f32`s, which range from [`Axis::MIN`] to [`Axis::MAX`], inclusive.
#[derive(Debug)]
#[derive(Debug, Resource)]
pub struct Axis<T> {
/// The position data of the input devices.
axis_data: HashMap<T, f32>,

View file

@ -1,6 +1,6 @@
use crate::{Axis, Input};
use bevy_ecs::event::{EventReader, EventWriter};
use bevy_ecs::system::{Res, ResMut};
use bevy_ecs::system::{Res, ResMut, Resource};
use bevy_utils::{tracing::info, HashMap, HashSet};
/// A gamepad with an associated `ID`.
@ -39,7 +39,7 @@ impl Gamepad {
/// The [`Gamepad`]s are registered and deregistered in the [`gamepad_connection_system`]
/// whenever a [`GamepadEventType::Connected`] or [`GamepadEventType::Disconnected`]
/// event is received.
#[derive(Default, Debug)]
#[derive(Resource, Default, Debug)]
pub struct Gamepads {
/// The collection of the connected [`Gamepad`]s.
gamepads: HashSet<Gamepad>,
@ -144,18 +144,19 @@ impl GamepadEvent {
/// # use bevy_input::gamepad::GamepadEventRaw;
/// # use bevy_app::prelude::*;
/// # use bevy_ecs::prelude::*;
/// #
/// // This system sets the `bool` resource to `true` if the `South` button
/// // of the first gamepad is pressed.
/// #[derive(Resource)]
/// struct MyResource(bool);
///
/// // This system sets the bool inside `MyResource` to `true` if the `South` button of the first gamepad is pressed.
/// fn change_resource_on_gamepad_button_press(
/// mut my_resource: ResMut<bool>,
/// mut my_resource: ResMut<MyResource>,
/// gamepads: Res<Gamepads>,
/// button_inputs: ResMut<Input<GamepadButton>>,
/// ) {
/// let gamepad = gamepads.iter().next().unwrap();
/// let gamepad_button= GamepadButton::new(*gamepad, GamepadButtonType::South);
///
/// *my_resource = button_inputs.pressed(gamepad_button);
/// my_resource.0 = button_inputs.pressed(gamepad_button);
/// }
///
/// // Create our app.
@ -163,7 +164,7 @@ impl GamepadEvent {
///
/// // Add the input plugin and the system/resource we want to test.
/// app.add_plugin(InputPlugin)
/// .insert_resource(false)
/// .insert_resource(MyResource(false))
/// .add_system(change_resource_on_gamepad_button_press);
///
/// // Define our dummy gamepad input data.
@ -185,8 +186,8 @@ impl GamepadEvent {
/// app.update();
///
/// // At this point you can check if your game logic corresponded correctly to the gamepad input.
/// // In this example we are checking if the `bool` resource was updated from `false` to `true`.
/// assert!(app.world.resource::<bool>());
/// // In this example we are checking if the bool in `MyResource` was updated from `false` to `true`.
/// assert!(app.world.resource::<MyResource>().0);
///
/// // Send the gamepad input event to mark the `South` gamepad button as released.
/// // This updates the `Input<GamepadButton>` resource accordingly.
@ -198,8 +199,8 @@ impl GamepadEvent {
/// // Advance the execution of the schedule by another cycle.
/// app.update();
///
/// // Check if the `bool` resource was updated from `true` to `false`.
/// assert!(!app.world.resource::<bool>());
/// // Check if the bool in `MyResource` was updated from `true` to `false`.
/// assert!(!app.world.resource::<MyResource>().0);
/// #
/// # bevy_ecs::system::assert_is_system(change_resource_on_gamepad_button_press);
/// ```
@ -391,7 +392,7 @@ impl GamepadAxis {
///
/// The [`GamepadSettings`] are used inside of the [`gamepad_event_system`], but are never written to
/// inside of `bevy`. To modify these settings, mutate the corresponding resource.
#[derive(Default, Debug)]
#[derive(Resource, Default, Debug)]
pub struct GamepadSettings {
/// The default button settings.
pub default_button_settings: ButtonSettings,

View file

@ -1,3 +1,4 @@
use bevy_ecs::system::Resource;
use bevy_utils::HashSet;
use std::hash::Hash;
@ -32,7 +33,7 @@ use bevy_ecs::schedule::State;
/// * Call the [`Input::press`] method for each press event.
/// * Call the [`Input::release`] method for each release event.
/// * Call the [`Input::clear`] method at each frame start, before processing events.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Resource)]
pub struct Input<T: Eq + Hash> {
/// A collection of every button that is currently being pressed.
pressed: HashSet<T>,

View file

@ -1,5 +1,5 @@
use bevy_ecs::event::EventReader;
use bevy_ecs::system::ResMut;
use bevy_ecs::system::{ResMut, Resource};
use bevy_math::Vec2;
use bevy_utils::HashMap;
@ -201,7 +201,7 @@ impl From<&TouchInput> for Touch {
/// ## Updating
///
/// The resource is updated inside of the [`touch_screen_input_system`](crate::touch::touch_screen_input_system).
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, Resource)]
pub struct Touches {
/// A collection of every [`Touch`] that is currently being pressed.
pressed: HashMap<u64, Touch>,

View file

@ -14,6 +14,7 @@ trace = [ "tracing-error" ]
[dependencies]
bevy_app = { path = "../bevy_app", version = "0.9.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.9.0-dev" }
tracing-subscriber = {version = "0.3.1", features = ["registry", "env-filter"]}
tracing-chrome = { version = "0.4.0", optional = true }

View file

@ -30,6 +30,8 @@ pub use bevy_utils::tracing::{
Level,
};
use bevy_ecs::prelude::Resource;
use bevy_app::{App, Plugin};
use tracing_log::LogTracer;
#[cfg(feature = "tracing-chrome")]
@ -89,6 +91,7 @@ use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter};
pub struct LogPlugin;
/// `LogPlugin` settings
#[derive(Resource)]
pub struct LogSettings {
/// Filters logs using the [`EnvFilter`] format
pub filter: String,

View file

@ -23,6 +23,7 @@ bevy_render = { path = "../bevy_render", version = "0.9.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.9.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" }
bevy_window = { path = "../bevy_window", version = "0.9.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.9.0-dev" }
# other
bitflags = "1.2"

View file

@ -73,7 +73,7 @@ impl PointLight {
pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.6;
}
#[derive(Clone, Debug, Reflect)]
#[derive(Resource, Clone, Debug, Reflect)]
#[reflect(Resource)]
pub struct PointLightShadowMap {
pub size: usize,
@ -208,7 +208,7 @@ impl DirectionalLight {
pub const DEFAULT_SHADOW_NORMAL_BIAS: f32 = 0.6;
}
#[derive(Clone, Debug, Reflect)]
#[derive(Resource, Clone, Debug, Reflect)]
#[reflect(Resource)]
pub struct DirectionalLightShadowMap {
pub size: usize,
@ -224,7 +224,7 @@ impl Default for DirectionalLightShadowMap {
}
/// An ambient light, which lights the entire scene equally.
#[derive(Clone, Debug, ExtractResource, Reflect)]
#[derive(Resource, Clone, Debug, ExtractResource, Reflect)]
#[reflect(Resource)]
pub struct AmbientLight {
pub color: Color,
@ -773,7 +773,7 @@ impl PointLightAssignmentData {
}
}
#[derive(Default)]
#[derive(Resource, Default)]
pub struct GlobalVisiblePointLights {
entities: HashSet<Entity>,
}

View file

@ -5,6 +5,7 @@ use crate::{
use bevy_app::{App, Plugin};
use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle};
use bevy_core_pipeline::core_3d::{AlphaMask3d, Opaque3d, Transparent3d};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
entity::Entity,
event::EventReader,
@ -12,7 +13,7 @@ use bevy_ecs::{
schedule::ParallelSystemDescriptorCoercion,
system::{
lifetimeless::{Read, SQuery, SRes},
Commands, Local, Query, Res, ResMut, SystemParamItem,
Commands, Local, Query, Res, ResMut, Resource, SystemParamItem,
},
world::FromWorld,
};
@ -224,6 +225,7 @@ where
}
/// Render pipeline data for a given [`Material`].
#[derive(Resource)]
pub struct MaterialPipeline<M: Material> {
pub mesh_pipeline: MeshPipeline,
pub material_layout: BindGroupLayout,
@ -434,6 +436,7 @@ pub struct PreparedMaterial<T: Material> {
pub properties: MaterialProperties,
}
#[derive(Resource)]
struct ExtractedMaterials<M: Material> {
extracted: Vec<(Handle<M>, M)>,
removed: Vec<Handle<M>>,
@ -449,7 +452,14 @@ impl<M: Material> Default for ExtractedMaterials<M> {
}
/// Stores all prepared representations of [`Material`] assets for as long as they exist.
pub type RenderMaterials<T> = HashMap<Handle<T>, PreparedMaterial<T>>;
#[derive(Resource, Deref, DerefMut)]
pub struct RenderMaterials<T: Material>(pub HashMap<Handle<T>, PreparedMaterial<T>>);
impl<T: Material> Default for RenderMaterials<T> {
fn default() -> Self {
Self(Default::default())
}
}
/// This system extracts all created or modified assets of the corresponding [`Material`] type
/// into the "render world".

View file

@ -214,6 +214,7 @@ pub const MAX_UNIFORM_BUFFER_POINT_LIGHTS: usize = 256;
pub const MAX_DIRECTIONAL_LIGHTS: usize = 1;
pub const SHADOW_FORMAT: TextureFormat = TextureFormat::Depth32Float;
#[derive(Resource)]
pub struct ShadowPipeline {
pub view_layout: BindGroupLayout,
pub mesh_layout: BindGroupLayout,
@ -662,6 +663,7 @@ pub struct ViewLightsUniformOffset {
// at least that many are supported using this constant and SupportedBindingType::from_device()
pub const CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT: u32 = 3;
#[derive(Resource)]
pub struct GlobalLightMeta {
pub gpu_point_lights: GpuPointLights,
pub entity_to_index: HashMap<Entity, usize>,
@ -686,7 +688,7 @@ impl GlobalLightMeta {
}
}
#[derive(Default)]
#[derive(Resource, Default)]
pub struct LightMeta {
pub view_gpu_lights: DynamicUniformBuffer<GpuLights>,
pub shadow_view_bind_group: Option<BindGroup>,

View file

@ -165,7 +165,7 @@ pub fn extract_meshes(
commands.insert_or_spawn_batch(not_caster_commands);
}
#[derive(Debug, Default)]
#[derive(Resource, Debug, Default)]
pub struct ExtractedJoints {
pub buffer: Vec<Mat4>,
}
@ -247,7 +247,7 @@ pub fn extract_skinned_meshes(
commands.insert_or_spawn_batch(values);
}
#[derive(Clone)]
#[derive(Resource, Clone)]
pub struct MeshPipeline {
pub view_layout: BindGroupLayout,
pub mesh_layout: BindGroupLayout,
@ -651,6 +651,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
}
}
#[derive(Resource)]
pub struct MeshBindGroup {
pub normal: BindGroup,
pub skinned: Option<BindGroup>,
@ -706,6 +707,7 @@ pub fn queue_mesh_bind_group(
// ignoring the rest, whether they're valid for other dynamic offsets or not. This trick may
// be supported later in encase, and then we should make use of it.
#[derive(Resource)]
pub struct SkinnedMeshUniform {
pub buffer: BufferVec<Mat4>,
}

View file

@ -62,13 +62,14 @@ fn extract_wireframes(mut commands: Commands, query: Extract<Query<Entity, With<
#[reflect(Component, Default)]
pub struct Wireframe;
#[derive(Debug, Clone, Default, ExtractResource, Reflect)]
#[derive(Resource, Debug, Clone, Default, ExtractResource, Reflect)]
#[reflect(Resource)]
pub struct WireframeConfig {
/// Whether to show wireframes for all meshes. If `false`, only meshes with a [Wireframe] component will be rendered.
pub global: bool,
}
#[derive(Resource)]
pub struct WireframePipeline {
mesh_pipeline: MeshPipeline,
shader: Handle<Shader>,

View file

@ -69,6 +69,7 @@ impl<C: Component + ShaderType + WriteInto + Clone> Plugin for UniformComponentP
}
/// Stores all uniforms of the component type.
#[derive(Resource)]
pub struct ComponentUniforms<C: Component + ShaderType> {
uniforms: DynamicUniformBuffer<C>,
}

View file

@ -42,7 +42,7 @@ use crate::{
primitives::{CubemapFrusta, Frustum},
render_graph::RenderGraph,
render_resource::{PipelineCache, Shader, ShaderLoader},
renderer::render_system,
renderer::{render_system, RenderInstance},
texture::ImagePlugin,
view::{ViewPlugin, WindowRenderPlugin},
};
@ -91,9 +91,13 @@ pub enum RenderStage {
/// This resource is only available during [`RenderStage::Extract`] and not
/// during command application of that stage.
/// See [`Extract`] for more details.
#[derive(Default)]
#[derive(Resource, Default)]
pub struct MainWorld(World);
/// The Render App World. This is only available as a resource during the Extract step.
#[derive(Resource, Default)]
pub struct RenderWorld(World);
impl Deref for MainWorld {
type Target = World;
@ -194,7 +198,7 @@ impl Plugin for RenderPlugin {
)
.add_stage(RenderStage::Cleanup, SystemStage::parallel())
.init_resource::<RenderGraph>()
.insert_resource(instance)
.insert_resource(RenderInstance(instance))
.insert_resource(device)
.insert_resource(queue)
.insert_resource(adapter_info)
@ -323,7 +327,7 @@ impl Plugin for RenderPlugin {
/// A "scratch" world used to avoid allocating new worlds every frame when
/// swapping out the [`MainWorld`] for [`RenderStage::Extract`].
#[derive(Default)]
#[derive(Resource, Default)]
struct ScratchMainWorld(World);
/// Executes the [`Extract`](RenderStage::Extract) stage of the renderer.

View file

@ -1,6 +1,7 @@
use crate::{Extract, RenderApp, RenderStage};
use bevy_app::{App, Plugin};
use bevy_asset::{Asset, AssetEvent, Assets, Handle};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
prelude::*,
system::{StaticSystemParam, SystemParam, SystemParamItem},
@ -101,6 +102,7 @@ impl<A: RenderAsset> Plugin for RenderAssetPlugin<A> {
}
/// Temporarily stores the extracted and removed assets of the current frame.
#[derive(Resource)]
pub struct ExtractedAssets<A: RenderAsset> {
extracted: Vec<(Handle<A>, A::ExtractedAsset)>,
removed: Vec<Handle<A>>,
@ -117,7 +119,14 @@ impl<A: RenderAsset> Default for ExtractedAssets<A> {
/// Stores all GPU representations ([`RenderAsset::PreparedAssets`](RenderAsset::PreparedAsset))
/// of [`RenderAssets`](RenderAsset) as long as they exist.
pub type RenderAssets<A> = HashMap<Handle<A>, <A as RenderAsset>::PreparedAsset>;
#[derive(Resource, Deref, DerefMut)]
pub struct RenderAssets<A: RenderAsset>(HashMap<Handle<A>, A::PreparedAsset>);
impl<A: RenderAsset> Default for RenderAssets<A> {
fn default() -> Self {
Self(Default::default())
}
}
/// This system extracts all crated or modified assets of the corresponding [`RenderAsset`] type
/// into the "render world".
@ -155,6 +164,7 @@ fn extract_render_asset<A: RenderAsset>(
// TODO: consider storing inside system?
/// All assets that should be prepared next frame.
#[derive(Resource)]
pub struct PrepareNextFrameAssets<A: RenderAsset> {
assets: Vec<(Handle<A>, A::ExtractedAsset)>,
}

View file

@ -5,7 +5,7 @@ use crate::{
},
renderer::RenderContext,
};
use bevy_ecs::prelude::World;
use bevy_ecs::{prelude::World, system::Resource};
use bevy_utils::HashMap;
use std::{borrow::Cow, fmt::Debug};
@ -48,7 +48,7 @@ use super::EdgeExistence;
/// graph.add_node("output_node", MyNode);
/// graph.add_node_edge("output_node", "input_node").unwrap();
/// ```
#[derive(Default)]
#[derive(Resource, Default)]
pub struct RenderGraph {
nodes: HashMap<NodeId, NodeState>,
node_names: HashMap<Cow<'static, str>, NodeId>,

View file

@ -7,7 +7,8 @@ use bevy_ecs::{
all_tuples,
entity::Entity,
system::{
lifetimeless::SRes, ReadOnlySystemParamFetch, SystemParam, SystemParamItem, SystemState,
lifetimeless::SRes, ReadOnlySystemParamFetch, Resource, SystemParam, SystemParamItem,
SystemState,
},
world::World,
};
@ -100,6 +101,7 @@ impl<P: PhaseItem> DrawFunctionsInternal<P> {
/// Stores all draw functions for the [`PhaseItem`] type hidden behind a reader-writer lock.
/// To access them the [`DrawFunctions::read`] and [`DrawFunctions::write`] methods are used.
#[derive(Resource)]
pub struct DrawFunctions<P: PhaseItem> {
internal: RwLock<DrawFunctionsInternal<P>>,
}

View file

@ -10,8 +10,8 @@ use crate::{
Extract,
};
use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_ecs::event::EventReader;
use bevy_ecs::system::{Res, ResMut};
use bevy_ecs::{event::EventReader, system::Resource};
use bevy_utils::{default, tracing::error, Entry, HashMap, HashSet};
use std::{hash::Hash, iter::FusedIterator, mem, ops::Deref, sync::Arc};
use thiserror::Error;
@ -263,6 +263,7 @@ impl LayoutCache {
}
}
#[derive(Resource)]
pub struct PipelineCache {
layout_cache: LayoutCache,
shader_cache: ShaderCache,

View file

@ -6,6 +6,7 @@ use crate::{
VertexBufferLayout,
},
};
use bevy_ecs::system::Resource;
use bevy_utils::{
default, hashbrown::hash_map::RawEntryMut, tracing::error, Entry, HashMap, PreHashMap,
PreHashMapExt,
@ -18,6 +19,7 @@ pub trait SpecializedRenderPipeline {
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor;
}
#[derive(Resource)]
pub struct SpecializedRenderPipelines<S: SpecializedRenderPipeline> {
cache: HashMap<S::Key, CachedRenderPipelineId>,
}
@ -80,6 +82,7 @@ pub trait SpecializedMeshPipeline {
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError>;
}
#[derive(Resource)]
pub struct SpecializedMeshPipelines<S: SpecializedMeshPipeline> {
mesh_layout_cache:
PreHashMap<InnerMeshVertexBufferLayout, HashMap<S::Key, CachedRenderPipelineId>>,

View file

@ -1,6 +1,7 @@
mod graph_runner;
mod render_device;
use bevy_derive::{Deref, DerefMut};
use bevy_utils::tracing::{error, info, info_span};
pub use graph_runner::*;
pub use render_device::*;
@ -28,7 +29,7 @@ pub fn render_system(world: &mut World) {
if let Err(e) = RenderGraphRunner::run(
graph,
render_device.clone(), // TODO: is this clone really necessary?
render_queue,
&render_queue.0,
world,
) {
error!("Error running render graph:");
@ -84,11 +85,17 @@ pub fn render_system(world: &mut World) {
}
/// This queue is used to enqueue tasks for the GPU to execute asynchronously.
pub type RenderQueue = Arc<Queue>;
#[derive(Resource, Clone, Deref, DerefMut)]
pub struct RenderQueue(pub Arc<Queue>);
/// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`],
/// as well as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces).
pub type RenderInstance = Instance;
#[derive(Resource, Deref, DerefMut)]
pub struct RenderInstance(pub Instance);
/// The `AdapterInfo` of the adapter in use by the renderer.
#[derive(Resource, Clone, Deref, DerefMut)]
pub struct RenderAdapterInfo(pub AdapterInfo);
/// Initializes the renderer by retrieving and preparing the GPU instance, device and queue
/// for the specified backend.
@ -96,7 +103,7 @@ pub async fn initialize_renderer(
instance: &Instance,
options: &WgpuSettings,
request_adapter_options: &RequestAdapterOptions<'_>,
) -> (RenderDevice, RenderQueue, AdapterInfo) {
) -> (RenderDevice, RenderQueue, RenderAdapterInfo) {
let adapter = instance
.request_adapter(request_adapter_options)
.await
@ -245,7 +252,11 @@ pub async fn initialize_renderer(
.unwrap();
let device = Arc::new(device);
let queue = Arc::new(queue);
(RenderDevice::from(device), queue, adapter_info)
(
RenderDevice::from(device),
RenderQueue(queue),
RenderAdapterInfo(adapter_info),
)
}
/// The context with all information required to interact with the GPU.

View file

@ -2,13 +2,14 @@ use crate::render_resource::{
BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor,
RenderPipeline, Sampler, Texture,
};
use bevy_ecs::system::Resource;
use std::sync::Arc;
use wgpu::{util::DeviceExt, BufferAsyncError, BufferBindingType};
use super::RenderQueue;
/// This GPU device is responsible for the creation of most rendering and compute resources.
#[derive(Clone)]
#[derive(Resource, Clone)]
pub struct RenderDevice {
device: Arc<wgpu::Device>,
}

View file

@ -1,5 +1,6 @@
use std::borrow::Cow;
use bevy_ecs::system::Resource;
pub use wgpu::{Backends, Features as WgpuFeatures, Limits as WgpuLimits, PowerPreference};
/// Configures the priority used when automatically configuring the features/limits of `wgpu`.
@ -22,7 +23,7 @@ pub enum WgpuSettingsPriority {
/// NOTE: If you want to use [`Backends::GL`](Backends::GL) in a native app on Windows, you must
/// use [`ANGLE`](https://github.com/gfx-rs/wgpu#angle). This is because wgpu requires EGL to
/// create a GL context without a window and only ANGLE supports that.
#[derive(Clone)]
#[derive(Resource, Clone)]
pub struct WgpuSettings {
pub device_label: Option<Cow<'static, str>>,
pub backends: Option<Backends>,

View file

@ -1,6 +1,6 @@
use crate::{render_resource::*, texture::DefaultImageSampler};
use bevy_derive::Deref;
use bevy_ecs::prelude::FromWorld;
use bevy_ecs::{prelude::FromWorld, system::Resource};
use bevy_math::Vec2;
use wgpu::{Extent3d, TextureDimension, TextureFormat};
@ -14,7 +14,7 @@ use crate::{
/// which can be used in situations where an image was not explicitly defined. The most common
/// use case is [`AsBindGroup`] implementations (such as materials) that support optional textures.
/// [`FallbackImage`] defaults to a 1x1 fully white texture, making blending colors with it a no-op.
#[derive(Deref)]
#[derive(Resource, Deref)]
pub struct FallbackImage(GpuImage);
impl FromWorld for FallbackImage {

View file

@ -14,7 +14,7 @@ use crate::{
};
use bevy_asset::HandleUntyped;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
use bevy_ecs::system::{lifetimeless::SRes, Resource, SystemParamItem};
use bevy_math::Vec2;
use bevy_reflect::TypeUuid;
use std::hash::Hash;
@ -162,6 +162,7 @@ impl ImageSampler {
/// Global resource for [`Image`] settings.
///
/// Can be set via `insert_resource` during app initialization to change the default settings.
#[derive(Resource)]
pub struct ImageSettings {
/// The default image sampler to use when [`ImageSampler`] is set to `Default`.
pub default_sampler: wgpu::SamplerDescriptor<'static>,
@ -194,7 +195,7 @@ impl ImageSettings {
///
/// The [`ImageSettings`] resource can be set during app initialization to change the default
/// image sampler.
#[derive(Debug, Clone, Deref, DerefMut)]
#[derive(Resource, Debug, Clone, Deref, DerefMut)]
pub struct DefaultImageSampler(pub(crate) Sampler);
impl Default for Image {

View file

@ -2,7 +2,7 @@ use crate::{
render_resource::{Texture, TextureView},
renderer::RenderDevice,
};
use bevy_ecs::prelude::ResMut;
use bevy_ecs::{prelude::ResMut, system::Resource};
use bevy_utils::{Entry, HashMap};
use wgpu::{TextureDescriptor, TextureViewDescriptor};
@ -26,7 +26,7 @@ pub struct CachedTexture {
/// This resource caches textures that are created repeatedly in the rendering process and
/// are only required for one frame.
#[derive(Default)]
#[derive(Resource, Default)]
pub struct TextureCache {
textures: HashMap<wgpu::TextureDescriptor<'static>, Vec<CachedTextureMeta>>,
}

View file

@ -58,7 +58,7 @@ impl Plugin for ViewPlugin {
/// .insert_resource(Msaa { samples: 4 })
/// .run();
/// ```
#[derive(Clone, ExtractResource, Reflect)]
#[derive(Resource, Clone, ExtractResource, Reflect)]
#[reflect(Resource)]
pub struct Msaa {
/// The number of samples to run for Multi-Sample Anti-Aliasing. Higher numbers result in
@ -105,7 +105,7 @@ pub struct ViewUniform {
height: f32,
}
#[derive(Default)]
#[derive(Resource, Default)]
pub struct ViewUniforms {
pub uniforms: DynamicUniformBuffer<ViewUniform>,
}

View file

@ -12,7 +12,7 @@ use std::ops::{Deref, DerefMut};
use wgpu::TextureFormat;
/// Token to ensure a system runs on the main thread.
#[derive(Default)]
#[derive(Resource, Default)]
pub struct NonSendMarker;
pub struct WindowRenderPlugin;
@ -48,7 +48,7 @@ pub struct ExtractedWindow {
pub size_changed: bool,
}
#[derive(Default)]
#[derive(Default, Resource)]
pub struct ExtractedWindows {
pub windows: HashMap<WindowId, ExtractedWindow>,
}
@ -113,7 +113,7 @@ fn extract_windows(
}
}
#[derive(Default)]
#[derive(Resource, Default)]
pub struct WindowSurfaces {
surfaces: HashMap<WindowId, wgpu::Surface>,
/// List of windows that we have already called the initial `configure_surface` for

View file

@ -1,5 +1,6 @@
use crate::{serde::SceneSerializer, Scene, SceneSpawnError};
use anyhow::Result;
use bevy_app::AppTypeRegistry;
use bevy_ecs::{
entity::EntityMap,
reflect::{ReflectComponent, ReflectMapEntities},
@ -84,7 +85,7 @@ impl DynamicScene {
world: &mut World,
entity_map: &mut EntityMap,
) -> Result<(), SceneSpawnError> {
let registry = world.resource::<TypeRegistryArc>().clone();
let registry = world.resource::<AppTypeRegistry>().clone();
let type_registry = registry.read();
for scene_entity in &self.entities {

View file

@ -1,5 +1,6 @@
use crate::serde::SceneDeserializer;
use anyhow::Result;
use bevy_app::AppTypeRegistry;
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
use bevy_ecs::world::{FromWorld, World};
use bevy_reflect::TypeRegistryArc;
@ -13,9 +14,9 @@ pub struct SceneLoader {
impl FromWorld for SceneLoader {
fn from_world(world: &mut World) -> Self {
let type_registry = world.resource::<TypeRegistryArc>();
let type_registry = world.resource::<AppTypeRegistry>();
SceneLoader {
type_registry: (*type_registry).clone(),
type_registry: type_registry.0.clone(),
}
}
}

View file

@ -1,14 +1,14 @@
use crate::{DynamicScene, Scene};
use bevy_app::AppTypeRegistry;
use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_ecs::{
entity::{Entity, EntityMap},
event::{Events, ManualEventReader},
reflect::{ReflectComponent, ReflectMapEntities},
system::Command,
system::{Command, Resource},
world::{Mut, World},
};
use bevy_hierarchy::{AddChild, Parent};
use bevy_reflect::TypeRegistryArc;
use bevy_utils::{tracing::error, HashMap};
use thiserror::Error;
use uuid::Uuid;
@ -27,7 +27,7 @@ impl InstanceId {
}
}
#[derive(Default)]
#[derive(Default, Resource)]
pub struct SceneSpawner {
spawned_scenes: HashMap<Handle<Scene>, Vec<InstanceId>>,
spawned_dynamic_scenes: HashMap<Handle<DynamicScene>, Vec<InstanceId>>,
@ -164,7 +164,7 @@ impl SceneSpawner {
let mut instance_info = InstanceInfo {
entity_map: EntityMap::default(),
};
let type_registry = world.resource::<TypeRegistryArc>().clone();
let type_registry = world.resource::<AppTypeRegistry>().clone();
let type_registry = type_registry.read();
world.resource_scope(|world, scenes: Mut<Assets<Scene>>| {
let scene =

View file

@ -22,6 +22,7 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.9.0-dev", features = [
bevy_render = { path = "../bevy_render", version = "0.9.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.9.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.9.0-dev" }
# other
bytemuck = { version = "1.5", features = ["derive"] }

View file

@ -1,6 +1,7 @@
use bevy_app::{App, Plugin};
use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle};
use bevy_core_pipeline::core_2d::Transparent2d;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
entity::Entity,
event::EventReader,
@ -8,7 +9,7 @@ use bevy_ecs::{
schedule::ParallelSystemDescriptorCoercion,
system::{
lifetimeless::{Read, SQuery, SRes},
Commands, Local, Query, Res, ResMut, SystemParamItem,
Commands, Local, Query, Res, ResMut, Resource, SystemParamItem,
},
world::FromWorld,
};
@ -170,6 +171,7 @@ where
}
/// Render pipeline data for a given [`Material2d`]
#[derive(Resource)]
pub struct Material2dPipeline<M: Material2d> {
pub mesh2d_pipeline: Mesh2dPipeline,
pub material2d_layout: BindGroupLayout,
@ -373,6 +375,7 @@ pub struct PreparedMaterial2d<T: Material2d> {
pub key: T::Data,
}
#[derive(Resource)]
struct ExtractedMaterials2d<M: Material2d> {
extracted: Vec<(Handle<M>, M)>,
removed: Vec<Handle<M>>,
@ -388,7 +391,14 @@ impl<M: Material2d> Default for ExtractedMaterials2d<M> {
}
/// Stores all prepared representations of [`Material2d`] assets for as long as they exist.
pub type RenderMaterials2d<T> = HashMap<Handle<T>, PreparedMaterial2d<T>>;
#[derive(Resource, Deref, DerefMut)]
pub struct RenderMaterials2d<T: Material2d>(HashMap<Handle<T>, PreparedMaterial2d<T>>);
impl<T: Material2d> Default for RenderMaterials2d<T> {
fn default() -> Self {
Self(Default::default())
}
}
/// This system extracts all created or modified assets of the corresponding [`Material2d`] type
/// into the "render world".

View file

@ -148,7 +148,7 @@ pub fn extract_mesh2d(
commands.insert_or_spawn_batch(values);
}
#[derive(Clone)]
#[derive(Resource, Clone)]
pub struct Mesh2dPipeline {
pub view_layout: BindGroupLayout,
pub mesh_layout: BindGroupLayout,
@ -379,6 +379,7 @@ impl SpecializedMeshPipeline for Mesh2dPipeline {
}
}
#[derive(Resource)]
pub struct Mesh2dBindGroup {
pub value: BindGroup,
}

View file

@ -34,6 +34,7 @@ use bytemuck::{Pod, Zeroable};
use copyless::VecHelper;
use fixedbitset::FixedBitSet;
#[derive(Resource)]
pub struct SpritePipeline {
view_layout: BindGroupLayout,
material_layout: BindGroupLayout,
@ -190,12 +191,12 @@ pub struct ExtractedSprite {
pub anchor: Vec2,
}
#[derive(Default)]
#[derive(Resource, Default)]
pub struct ExtractedSprites {
pub sprites: Vec<ExtractedSprite>,
}
#[derive(Default)]
#[derive(Resource, Default)]
pub struct SpriteAssetEvents {
pub images: Vec<AssetEvent<Image>>,
}
@ -303,6 +304,7 @@ struct ColoredSpriteVertex {
pub color: [f32; 4],
}
#[derive(Resource)]
pub struct SpriteMeta {
vertices: BufferVec<SpriteVertex>,
colored_vertices: BufferVec<ColoredSpriteVertex>,
@ -341,7 +343,7 @@ pub struct SpriteBatch {
colored: bool,
}
#[derive(Default)]
#[derive(Resource, Default)]
pub struct ImageBindGroups {
values: HashMap<Handle<Image>, BindGroup>,
}

View file

@ -2,6 +2,7 @@ use std::hash::Hash;
use ab_glyph::{PxScale, ScaleFont};
use bevy_asset::{Assets, Handle, HandleId};
use bevy_ecs::system::Resource;
use bevy_math::Vec2;
use bevy_render::texture::Image;
use bevy_sprite::TextureAtlas;
@ -14,6 +15,7 @@ use crate::{
TextAlignment, TextSection,
};
#[derive(Resource)]
pub struct TextPipeline<ID> {
brush: GlyphBrush,
glyph_map: HashMap<ID, TextLayoutInfo>,

View file

@ -4,7 +4,7 @@ use bevy_ecs::{
component::ComponentId,
query::Access,
schedule::ShouldRun,
system::{IntoSystem, Res, ResMut, System},
system::{IntoSystem, Res, ResMut, Resource, System},
world::World,
};
use bevy_utils::HashMap;
@ -41,7 +41,7 @@ impl FixedTimestepState {
/// A global resource that tracks the individual [`FixedTimestepState`]s
/// for every labeled [`FixedTimestep`].
#[derive(Default)]
#[derive(Default, Resource)]
pub struct FixedTimesteps {
fixed_timesteps: HashMap<String, FixedTimestepState>,
}
@ -234,7 +234,8 @@ mod test {
use std::ops::{Add, Mul};
use std::time::Duration;
type Count = usize;
#[derive(Resource)]
struct Count(usize);
const LABEL: &str = "test_step";
#[test]
@ -245,7 +246,7 @@ mod test {
time.update_with_instant(instance);
world.insert_resource(time);
world.insert_resource(FixedTimesteps::default());
world.insert_resource::<Count>(0);
world.insert_resource(Count(0));
let mut schedule = Schedule::default();
schedule.add_stage(
@ -258,30 +259,30 @@ mod test {
// if time does not progress, the step does not run
schedule.run(&mut world);
schedule.run(&mut world);
assert_eq!(0, *world.resource::<Count>());
assert_eq!(0, world.resource::<Count>().0);
assert_eq!(0., get_accumulator_deciseconds(&world));
// let's progress less than one step
advance_time(&mut world, instance, 0.4);
schedule.run(&mut world);
assert_eq!(0, *world.resource::<Count>());
assert_eq!(0, world.resource::<Count>().0);
assert_eq!(4., get_accumulator_deciseconds(&world));
// finish the first step with 0.1s above the step length
advance_time(&mut world, instance, 0.6);
schedule.run(&mut world);
assert_eq!(1, *world.resource::<Count>());
assert_eq!(1, world.resource::<Count>().0);
assert_eq!(1., get_accumulator_deciseconds(&world));
// runs multiple times if the delta is multiple step lengths
advance_time(&mut world, instance, 1.7);
schedule.run(&mut world);
assert_eq!(3, *world.resource::<Count>());
assert_eq!(3, world.resource::<Count>().0);
assert_eq!(2., get_accumulator_deciseconds(&world));
}
fn fixed_update(mut count: ResMut<Count>) {
*count += 1;
count.0 += 1;
}
fn advance_time(world: &mut World, instance: Instant, seconds: f32) {

View file

@ -46,8 +46,11 @@ impl Plugin for TimePlugin {
}
/// Channel resource used to receive time from render world
#[derive(Resource)]
pub struct TimeReceiver(pub Receiver<Instant>);
/// Channel resource used to send time from render world
#[derive(Resource)]
pub struct TimeSender(pub Sender<Instant>);
/// Creates channels used for sending time between render world and app world

View file

@ -1,9 +1,9 @@
use bevy_ecs::reflect::ReflectResource;
use bevy_ecs::{reflect::ReflectResource, system::Resource};
use bevy_reflect::Reflect;
use bevy_utils::{Duration, Instant};
/// Tracks elapsed time since the last update and since the App has started
#[derive(Reflect, Debug, Clone)]
#[derive(Resource, Reflect, Debug, Clone)]
#[reflect(Resource)]
pub struct Time {
delta: Duration,
@ -54,6 +54,7 @@ impl Time {
/// # fn main () {
/// # test_health_system();
/// # }
/// #[derive(Resource)]
/// struct Health {
/// // Health value between 0.0 and 1.0
/// health_value: f32,

View file

@ -5,7 +5,7 @@ use bevy_ecs::{
entity::Entity,
event::EventReader,
query::{Changed, With, Without, WorldQuery},
system::{Query, Res, ResMut},
system::{Query, Res, ResMut, Resource},
};
use bevy_hierarchy::{Children, Parent};
use bevy_log::warn;
@ -16,6 +16,7 @@ use bevy_window::{Window, WindowId, WindowScaleFactorChanged, Windows};
use std::fmt;
use taffy::{number::Number, Taffy};
#[derive(Resource)]
pub struct FlexSurface {
entity_to_taffy: HashMap<Entity, taffy::node::Node>,
window_nodes: HashMap<WindowId, taffy::node::Node>,

View file

@ -168,7 +168,7 @@ pub struct ExtractedUiNode {
pub clip: Option<Rect>,
}
#[derive(Default)]
#[derive(Resource, Default)]
pub struct ExtractedUiNodes {
pub uinodes: Vec<ExtractedUiNode>,
}
@ -341,6 +341,7 @@ struct UiVertex {
pub color: [f32; 4],
}
#[derive(Resource)]
pub struct UiMeta {
vertices: BufferVec<UiVertex>,
view_bind_group: Option<BindGroup>,
@ -504,7 +505,7 @@ pub fn prepare_uinodes(
ui_meta.vertices.write_buffer(&render_device, &render_queue);
}
#[derive(Default)]
#[derive(Resource, Default)]
pub struct UiImageBindGroups {
pub values: HashMap<Handle<Image>, BindGroup>,
}

View file

@ -3,6 +3,7 @@ use bevy_render::{
render_resource::*, renderer::RenderDevice, texture::BevyDefault, view::ViewUniform,
};
#[derive(Resource)]
pub struct UiPipeline {
pub view_layout: BindGroupLayout,
pub image_layout: BindGroupLayout,

View file

@ -25,13 +25,14 @@ use bevy_app::prelude::*;
use bevy_ecs::{
event::Events,
schedule::{ParallelSystemDescriptorCoercion, SystemLabel},
system::Resource,
};
/// The configuration information for the [`WindowPlugin`].
///
/// It can be added as a [`Resource`](bevy_ecs::system::Resource) before the [`WindowPlugin`]
/// runs, to configure how it behaves.
#[derive(Clone)]
#[derive(Resource, Clone)]
pub struct WindowSettings {
/// Whether to create a window when added.
///

View file

@ -1,3 +1,4 @@
use bevy_ecs::system::Resource;
use bevy_math::{DVec2, IVec2, UVec2, Vec2};
use bevy_reflect::{FromReflect, Reflect};
use bevy_utils::{tracing::warn, Uuid};
@ -777,7 +778,7 @@ pub enum MonitorSelection {
/// See [`examples/window/window_settings.rs`] for usage.
///
/// [`examples/window/window_settings.rs`]: https://github.com/bevyengine/bevy/blob/latest/examples/window/window_settings.rs
#[derive(Debug, Clone)]
#[derive(Resource, Debug, Clone)]
pub struct WindowDescriptor {
/// The requested logical width of the window's client area.
///

View file

@ -1,8 +1,9 @@
use super::{Window, WindowId};
use bevy_ecs::prelude::Resource;
use bevy_utils::HashMap;
/// A collection of [`Window`]s with unique [`WindowId`]s.
#[derive(Debug, Default)]
#[derive(Debug, Default, Resource)]
pub struct Windows {
windows: HashMap<WindowId, Window>,
}

View file

@ -316,7 +316,7 @@ impl Default for WinitPersistentState {
}
}
#[derive(Default)]
#[derive(Default, Resource)]
struct WinitCreateWindowReader(ManualEventReader<CreateWindow>);
pub fn winit_runner_with(mut app: App) {

View file

@ -20,13 +20,14 @@ struct ResizeEvent {
window_id: WindowId,
}
#[derive(Resource)]
pub(crate) struct CanvasParentResizeEventChannel {
sender: Sender<ResizeEvent>,
receiver: Receiver<ResizeEvent>,
}
fn canvas_parent_resize_event_handler(
winit_windows: Res<WinitWindows>,
winit_windows: NonSend<WinitWindows>,
resize_events: Res<CanvasParentResizeEventChannel>,
) {
for event in resize_events.receiver.try_iter() {

View file

@ -1,7 +1,8 @@
use bevy_ecs::system::Resource;
use bevy_utils::Duration;
/// A resource for configuring usage of the `rust_winit` library.
#[derive(Debug)]
#[derive(Debug, Resource)]
pub struct WinitSettings {
/// Configures the winit library to return control to the main thread after the
/// [run](bevy_app::App::run) loop is exited. Winit strongly recommends avoiding this when

View file

@ -16,6 +16,7 @@ fn main() {
.run();
}
#[derive(Resource)]
struct MyEntity(Entity);
#[derive(Component)]

View file

@ -116,6 +116,7 @@ fn star(
pub struct ColoredMesh2d;
/// Custom pipeline for 2d meshes with vertex colors
#[derive(Resource)]
pub struct ColoredMesh2dPipeline {
/// this pipeline wraps the standard [`Mesh2dPipeline`]
mesh2d_pipeline: Mesh2dPipeline,

View file

@ -21,7 +21,7 @@ enum AppState {
Finished,
}
#[derive(Default)]
#[derive(Resource, Default)]
struct RpgSpriteHandles {
handles: Vec<HandleUntyped>,
}

View file

@ -52,6 +52,7 @@ fn main() {
.run();
}
#[derive(Resource)]
struct Cubemap {
is_loaded: bool,
index: usize,

View file

@ -15,6 +15,7 @@ fn main() {
.run();
}
#[derive(Resource)]
struct Animations(Vec<Handle<AnimationClip>>);
fn setup(

View file

@ -4,6 +4,7 @@
use bevy::prelude::*;
use std::io;
#[derive(Resource)]
struct Input(String);
fn my_runner(mut app: App) {

View file

@ -35,6 +35,7 @@ impl Plugin for PrintMessagePlugin {
}
}
#[derive(Resource)]
struct PrintMessageState {
message: String,
timer: Timer,

View file

@ -46,7 +46,7 @@ fn main() {
.run();
}
#[derive(Default)]
#[derive(Resource, Default)]
struct State {
handle: Handle<CustomAsset>,
printed: bool,

View file

@ -22,9 +22,10 @@ fn main() {
// Number of cubes to spawn across the x, y, and z axis
const NUM_CUBES: u32 = 6;
#[derive(Deref)]
#[derive(Resource, Deref)]
struct BoxMeshHandle(Handle<Mesh>);
#[derive(Deref)]
#[derive(Resource, Deref)]
struct BoxMaterialHandle(Handle<StandardMaterial>);
/// Startup system which runs only once and generates our Box Mesh

Some files were not shown because too many files have changed in this diff Show more