Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace all labels with interned labels #7762

Merged
merged 55 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
431a27e
Replace boxed labels with interned labels
geieredgar Jul 22, 2023
777b394
Implement `From` instead of `Into`
geieredgar Jul 22, 2023
369cf43
Use `from` instead of `into`
geieredgar Jul 22, 2023
5277f1a
Fix invariant descriptions
geieredgar Jul 22, 2023
1ac419a
Reuse anonymous set ids between different schedules
geieredgar Jul 23, 2023
9258450
Make all labels internable
geieredgar Jul 22, 2023
556a8ac
Merge `OptimizedInterner` into `Interner`
geieredgar Jul 23, 2023
0f6596e
remove `Borrow` from `Leak`
JoJoJet Jul 27, 2023
e9e16fb
merge `StaticRef` with `Leak`
JoJoJet Jul 27, 2023
0d9d83b
make a note private
JoJoJet Jul 27, 2023
fff9864
interned labels are labels
JoJoJet Jul 27, 2023
803fa9c
simplify interning APIs
JoJoJet Jul 27, 2023
3099a4b
rustfmt
JoJoJet Jul 28, 2023
815b52c
special-case interned system sets
JoJoJet Jul 28, 2023
ab0e722
remove an unnecessary special-case
JoJoJet Jul 28, 2023
681b470
Overide `.intern()` for `Interned<dyn Label>`
JoJoJet Jul 28, 2023
6c61187
Remove use of `std::borrow::Borrow`
geieredgar Jul 28, 2023
73c05c9
Fix clippy warnings
geieredgar Jul 28, 2023
531bb9b
Improve documentation
geieredgar Jul 28, 2023
53e6e2c
Fix whitespace
geieredgar Jul 28, 2023
c664fe7
Fix docs
geieredgar Jul 28, 2023
a4d7c81
Update crates/bevy_utils/src/intern.rs
geieredgar Aug 5, 2023
11fcbd0
Fix and add tests for label derive macros
geieredgar Aug 5, 2023
b5130c7
Replace `::bevy_utils` with `$crate`
geieredgar Aug 5, 2023
c190414
Fix macro hygiene
geieredgar Aug 5, 2023
db71468
Fix typos
geieredgar Aug 5, 2023
75b2a46
Disallow deriving label and set traits on unions
geieredgar Aug 5, 2023
f596145
Remove code duplication
geieredgar Aug 5, 2023
04dbbeb
Simplify fallback variant construction
geieredgar Aug 5, 2023
806b0a5
Remove headings
geieredgar Aug 5, 2023
afc0a2d
Fix derive macro docs
geieredgar Aug 5, 2023
629e02c
Refactor `static_ref_impl`
geieredgar Aug 5, 2023
3f769e5
Test derive for generic labels and sets
geieredgar Aug 5, 2023
8ea3a7f
Simplify `static_ref_impl` for enum types
geieredgar Aug 5, 2023
cbf145b
Fix `static_ref_impl` for empty struct and tuple variants
geieredgar Aug 5, 2023
8efc7a1
Fix formating
geieredgar Aug 5, 2023
a501af7
Simplify `static_ref_impl` again
geieredgar Aug 5, 2023
1d1b8a9
Use static items to ensure returning the same static reference
geieredgar Aug 9, 2023
5a43a0c
Unify `derive_set` and `derive_label`, reexport and use `DynEq`
geieredgar Aug 13, 2023
65a54aa
Remove obsolete import of `bevy_utils`
geieredgar Aug 13, 2023
99f7cfc
Add names to blocks for extra methods
geieredgar Aug 26, 2023
4286b3c
Fix clippy warning
geieredgar Aug 26, 2023
ba6d135
Fix docs
geieredgar Aug 26, 2023
a05e6f1
Merge branch 'main' into interned-labels
geieredgar Oct 1, 2023
83c2b8b
Fix errors after merging
geieredgar Oct 1, 2023
563e392
Remove `static_ref` optimization
geieredgar Oct 1, 2023
6a3760f
Add unit distinction test
geieredgar Oct 1, 2023
2af81e9
Remove proc-macro2 dependency (leftover from static_ref optimization)
geieredgar Oct 1, 2023
846734a
Add and use `ref_eq` instead of relying on `std::ptr::eq`
geieredgar Oct 1, 2023
8d10df7
Fix missing `;`
geieredgar Oct 1, 2023
4b1a9fd
Fix test
geieredgar Oct 1, 2023
813dd42
Fix doctest
geieredgar Oct 1, 2023
83ee170
Fix doc link
geieredgar Oct 1, 2023
2d75e20
Merge branch 'main' into interned-labels
geieredgar Oct 18, 2023
e2acc8b
Flip comparison order
cart Oct 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 137 additions & 25 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use bevy_ecs::{
prelude::*,
schedule::{
apply_state_transition, common_conditions::run_once as run_once_condition,
run_enter_schedule, BoxedScheduleLabel, IntoSystemConfigs, IntoSystemSetConfigs,
run_enter_schedule, InternedScheduleLabel, IntoSystemConfigs, IntoSystemSetConfigs,
ScheduleBuildSettings, ScheduleLabel,
},
};
use bevy_utils::{tracing::debug, HashMap, HashSet};
use bevy_utils::{intern::Interned, tracing::debug, HashMap, HashSet};
use std::{
fmt::Debug,
panic::{catch_unwind, resume_unwind, AssertUnwindSafe},
Expand All @@ -20,10 +20,14 @@ use bevy_utils::tracing::info_span;
bevy_utils::define_label!(
/// A strongly-typed class of labels used to identify an [`App`].
AppLabel,
/// A strongly-typed identifier for an [`AppLabel`].
AppLabelId,
APP_LABEL_INTERNER
);

pub use bevy_utils::label::DynEq;

/// A shorthand for `Interned<dyn AppLabel>`.
pub type InternedAppLabel = Interned<dyn AppLabel>;

pub(crate) enum AppError {
DuplicatePlugin { plugin_name: String },
}
Expand Down Expand Up @@ -70,8 +74,8 @@ pub struct App {
/// The schedule that runs the main loop of schedule execution.
///
/// This is initially set to [`Main`].
pub main_schedule_label: BoxedScheduleLabel,
sub_apps: HashMap<AppLabelId, SubApp>,
pub main_schedule_label: InternedScheduleLabel,
sub_apps: HashMap<InternedAppLabel, SubApp>,
plugin_registry: Vec<Box<dyn Plugin>>,
plugin_name_added: HashSet<String>,
/// A private counter to prevent incorrect calls to `App::run()` from `Plugin::build()`
Expand Down Expand Up @@ -156,7 +160,7 @@ impl SubApp {

/// Runs the [`SubApp`]'s default schedule.
pub fn run(&mut self) {
self.app.world.run_schedule(&*self.app.main_schedule_label);
self.app.world.run_schedule(self.app.main_schedule_label);
self.app.world.clear_trackers();
}

Expand Down Expand Up @@ -219,7 +223,7 @@ impl App {
sub_apps: HashMap::default(),
plugin_registry: Vec::default(),
plugin_name_added: Default::default(),
main_schedule_label: Box::new(Main),
main_schedule_label: Main.intern(),
building_plugin_depth: 0,
}
}
Expand All @@ -241,7 +245,7 @@ impl App {
{
#[cfg(feature = "trace")]
let _bevy_main_update_span = info_span!("main app").entered();
self.world.run_schedule(&*self.main_schedule_label);
self.world.run_schedule(self.main_schedule_label);
}
for (_label, sub_app) in &mut self.sub_apps {
#[cfg(feature = "trace")]
Expand Down Expand Up @@ -379,9 +383,10 @@ impl App {
schedule: impl ScheduleLabel,
systems: impl IntoSystemConfigs<M>,
) -> &mut Self {
let schedule = schedule.intern();
let mut schedules = self.world.resource_mut::<Schedules>();

if let Some(schedule) = schedules.get_mut(&schedule) {
if let Some(schedule) = schedules.get_mut(schedule) {
schedule.add_systems(systems);
} else {
let mut new_schedule = Schedule::new(schedule);
Expand Down Expand Up @@ -410,8 +415,9 @@ impl App {
schedule: impl ScheduleLabel,
sets: impl IntoSystemSetConfigs,
) -> &mut Self {
let schedule = schedule.intern();
let mut schedules = self.world.resource_mut::<Schedules>();
if let Some(schedule) = schedules.get_mut(&schedule) {
if let Some(schedule) = schedules.get_mut(schedule) {
schedule.configure_sets(sets);
} else {
let mut new_schedule = Schedule::new(schedule);
Expand Down Expand Up @@ -746,16 +752,15 @@ impl App {
pub fn sub_app_mut(&mut self, label: impl AppLabel) -> &mut App {
match self.get_sub_app_mut(label) {
Ok(app) => app,
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label.as_str()),
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label),
}
}

/// Retrieves a `SubApp` inside this [`App`] with the given label, if it exists. Otherwise returns
/// an [`Err`] containing the given label.
pub fn get_sub_app_mut(&mut self, label: impl AppLabel) -> Result<&mut App, AppLabelId> {
let label = label.as_label();
pub fn get_sub_app_mut(&mut self, label: impl AppLabel) -> Result<&mut App, impl AppLabel> {
self.sub_apps
.get_mut(&label)
.get_mut(&label.intern())
.map(|sub_app| &mut sub_app.app)
.ok_or(label)
}
Expand All @@ -768,25 +773,25 @@ impl App {
pub fn sub_app(&self, label: impl AppLabel) -> &App {
match self.get_sub_app(label) {
Ok(app) => app,
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label.as_str()),
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label),
}
}

/// Inserts an existing sub app into the app
pub fn insert_sub_app(&mut self, label: impl AppLabel, sub_app: SubApp) {
self.sub_apps.insert(label.as_label(), sub_app);
self.sub_apps.insert(label.intern(), sub_app);
}

/// Removes a sub app from the app. Returns [`None`] if the label doesn't exist.
pub fn remove_sub_app(&mut self, label: impl AppLabel) -> Option<SubApp> {
self.sub_apps.remove(&label.as_label())
self.sub_apps.remove(&label.intern())
}

/// Retrieves a `SubApp` inside this [`App`] with the given label, if it exists. Otherwise returns
/// an [`Err`] containing the given label.
pub fn get_sub_app(&self, label: impl AppLabel) -> Result<&App, impl AppLabel> {
self.sub_apps
.get(&label.as_label())
.get(&label.intern())
.map(|sub_app| &sub_app.app)
.ok_or(label)
}
Expand All @@ -807,8 +812,9 @@ impl App {
///
/// See [`App::add_schedule`] to pass in a pre-constructed schedule.
pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self {
let label = label.intern();
let mut schedules = self.world.resource_mut::<Schedules>();
if !schedules.contains(&label) {
if !schedules.contains(label) {
schedules.insert(Schedule::new(label));
}
self
Expand All @@ -817,15 +823,15 @@ impl App {
/// Gets read-only access to the [`Schedule`] with the provided `label` if it exists.
pub fn get_schedule(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
let schedules = self.world.get_resource::<Schedules>()?;
schedules.get(&label)
schedules.get(label)
}

/// Gets read-write access to a [`Schedule`] with the provided `label` if it exists.
pub fn get_schedule_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
let schedules = self.world.get_resource_mut::<Schedules>()?;
// We need to call .into_inner here to satisfy the borrow checker:
// it can reason about reborrows using ordinary references but not the `Mut` smart pointer.
schedules.into_inner().get_mut(&label)
schedules.into_inner().get_mut(label)
}

/// Applies the function to the [`Schedule`] associated with `label`.
Expand All @@ -836,13 +842,14 @@ impl App {
label: impl ScheduleLabel,
f: impl FnOnce(&mut Schedule),
) -> &mut Self {
let label = label.intern();
let mut schedules = self.world.resource_mut::<Schedules>();

if schedules.get(&label).is_none() {
schedules.insert(Schedule::new(label.dyn_clone()));
if schedules.get(label).is_none() {
schedules.insert(Schedule::new(label));
}

let schedule = schedules.get_mut(&label).unwrap();
let schedule = schedules.get_mut(label).unwrap();
// Call the function f, passing in the schedule retrieved
f(schedule);

Expand Down Expand Up @@ -964,6 +971,8 @@ pub struct AppExit;

#[cfg(test)]
mod tests {
use std::marker::PhantomData;

use bevy_ecs::{
schedule::{OnEnter, States},
system::Commands,
Expand Down Expand Up @@ -1060,4 +1069,107 @@ mod tests {
app.world.run_schedule(OnEnter(AppState::MainMenu));
assert_eq!(app.world.entities().len(), 2);
}

#[test]
fn test_derive_app_label() {
use super::AppLabel;
use crate::{self as bevy_app};

#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct UnitLabel;

#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct TupleLabel(u32, u32);

#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct StructLabel {
a: u32,
b: u32,
}

#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct EmptyTupleLabel();

#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct EmptyStructLabel {}

#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
enum EnumLabel {
#[default]
Unit,
Tuple(u32, u32),
Struct {
a: u32,
b: u32,
},
}

#[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
struct GenericLabel<T>(PhantomData<T>);

assert_eq!(UnitLabel.intern(), UnitLabel.intern());
assert_eq!(EnumLabel::Unit.intern(), EnumLabel::Unit.intern());
assert_ne!(UnitLabel.intern(), EnumLabel::Unit.intern());
assert_ne!(UnitLabel.intern(), TupleLabel(0, 0).intern());
assert_ne!(EnumLabel::Unit.intern(), EnumLabel::Tuple(0, 0).intern());

assert_eq!(TupleLabel(0, 0).intern(), TupleLabel(0, 0).intern());
assert_eq!(
EnumLabel::Tuple(0, 0).intern(),
EnumLabel::Tuple(0, 0).intern()
);
assert_ne!(TupleLabel(0, 0).intern(), TupleLabel(0, 1).intern());
assert_ne!(
EnumLabel::Tuple(0, 0).intern(),
EnumLabel::Tuple(0, 1).intern()
);
assert_ne!(TupleLabel(0, 0).intern(), EnumLabel::Tuple(0, 0).intern());
assert_ne!(
TupleLabel(0, 0).intern(),
StructLabel { a: 0, b: 0 }.intern()
);
assert_ne!(
EnumLabel::Tuple(0, 0).intern(),
EnumLabel::Struct { a: 0, b: 0 }.intern()
);

assert_eq!(
StructLabel { a: 0, b: 0 }.intern(),
StructLabel { a: 0, b: 0 }.intern()
);
assert_eq!(
EnumLabel::Struct { a: 0, b: 0 }.intern(),
EnumLabel::Struct { a: 0, b: 0 }.intern()
);
assert_ne!(
StructLabel { a: 0, b: 0 }.intern(),
StructLabel { a: 0, b: 1 }.intern()
);
assert_ne!(
EnumLabel::Struct { a: 0, b: 0 }.intern(),
EnumLabel::Struct { a: 0, b: 1 }.intern()
);
assert_ne!(
StructLabel { a: 0, b: 0 }.intern(),
EnumLabel::Struct { a: 0, b: 0 }.intern()
);
assert_ne!(
StructLabel { a: 0, b: 0 }.intern(),
EnumLabel::Struct { a: 0, b: 0 }.intern()
);
assert_ne!(StructLabel { a: 0, b: 0 }.intern(), UnitLabel.intern(),);
assert_ne!(
EnumLabel::Struct { a: 0, b: 0 }.intern(),
EnumLabel::Unit.intern()
);

assert_eq!(
GenericLabel::<u32>(PhantomData).intern(),
GenericLabel::<u32>(PhantomData).intern()
);
assert_ne!(
GenericLabel::<u32>(PhantomData).intern(),
GenericLabel::<u64>(PhantomData).intern()
);
}
}
26 changes: 13 additions & 13 deletions crates/bevy_app/src/main_schedule.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{App, Plugin};
use bevy_ecs::{
schedule::{ExecutorKind, Schedule, ScheduleLabel},
schedule::{ExecutorKind, InternedScheduleLabel, Schedule, ScheduleLabel},
system::{Local, Resource},
world::{Mut, World},
};
Expand Down Expand Up @@ -105,21 +105,21 @@ pub struct Last;
#[derive(Resource, Debug)]
pub struct MainScheduleOrder {
/// The labels to run for the [`Main`] schedule (in the order they will be run).
pub labels: Vec<Box<dyn ScheduleLabel>>,
pub labels: Vec<InternedScheduleLabel>,
}

impl Default for MainScheduleOrder {
fn default() -> Self {
Self {
labels: vec![
Box::new(First),
Box::new(PreUpdate),
Box::new(StateTransition),
Box::new(RunFixedUpdateLoop),
Box::new(Update),
Box::new(SpawnScene),
Box::new(PostUpdate),
Box::new(Last),
First.intern(),
PreUpdate.intern(),
StateTransition.intern(),
RunFixedUpdateLoop.intern(),
Update.intern(),
SpawnScene.intern(),
PostUpdate.intern(),
Last.intern(),
],
}
}
Expand All @@ -133,7 +133,7 @@ impl MainScheduleOrder {
.iter()
.position(|current| (**current).eq(&after))
.unwrap_or_else(|| panic!("Expected {after:?} to exist"));
self.labels.insert(index + 1, Box::new(schedule));
self.labels.insert(index + 1, schedule.intern());
}
}

Expand All @@ -148,8 +148,8 @@ impl Main {
}

world.resource_scope(|world, order: Mut<MainScheduleOrder>| {
for label in &order.labels {
let _ = world.try_run_schedule(&**label);
for &label in &order.labels {
let _ = world.try_run_schedule(label);
}
});
}
Expand Down
9 changes: 5 additions & 4 deletions crates/bevy_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,13 @@ pub fn derive_enum_variant_meta(input: TokenStream) -> TokenStream {

/// Generates an impl of the `AppLabel` trait.
///
/// This works only for unit structs, or enums with only unit variants.
/// You may force a struct or variant to behave as if it were fieldless with `#[app_label(ignore_fields)]`.
#[proc_macro_derive(AppLabel, attributes(app_label))]
/// This does not work for unions.
#[proc_macro_derive(AppLabel)]
pub fn derive_app_label(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let mut trait_path = BevyManifest::default().get_path("bevy_app");
let mut dyn_eq_path = trait_path.clone();
trait_path.segments.push(format_ident!("AppLabel").into());
derive_label(input, &trait_path, "app_label")
dyn_eq_path.segments.push(format_ident!("DynEq").into());
derive_label(input, "AppLabel", &trait_path, &dyn_eq_path)
}
Loading