Skip to content

Commit c43295a

Browse files
committed
Simplify design for *Labels (#4957)
# Objective - Closes #4954 - Reduce the complexity of the `{System, App, *}Label` APIs. ## Solution For the sake of brevity I will only refer to `SystemLabel`, but everything applies to all of the other label types as well. - Add `SystemLabelId`, a lightweight, `copy` struct. - Convert custom types into `SystemLabelId` using the trait `SystemLabel`. ## Changelog - String literals implement `SystemLabel` for now, but this should be changed with #4409 . ## Migration Guide - Any previous use of `Box<dyn SystemLabel>` should be replaced with `SystemLabelId`. - `AsSystemLabel` trait has been modified. - No more output generics. - Method `as_system_label` now returns `SystemLabelId`, removing an unnecessary level of indirection. - If you *need* a label that is determined at runtime, you can use `Box::leak`. Not recommended. ## Questions for later * Should we generate a `Debug` impl along with `#[derive(*Label)]`? * Should we rename `as_str()`? * Should we remove the extra derives (such as `Hash`) from builtin `*Label` types? * Should we automatically derive types like `Clone, Copy, PartialEq, Eq`? * More-ergonomic comparisons between `Label` and `LabelId`. * Move `Dyn{Eq, Hash,Clone}` somewhere else. * Some API to make interning dynamic labels easier. * Optimize string representation * Empty string for unit structs -- no debug info but faster comparisons * Don't show enum types -- same tradeoffs as asbove.
1 parent 234e5af commit c43295a

File tree

15 files changed

+308
-317
lines changed

15 files changed

+308
-317
lines changed

benches/benches/bevy_ecs/scheduling/schedule.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,29 @@ pub fn build_schedule(criterion: &mut Criterion) {
6363

6464
// Use multiple different kinds of label to ensure that dynamic dispatch
6565
// doesn't somehow get optimized away.
66-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemLabel)]
66+
#[derive(Debug, Clone, Copy)]
6767
struct NumLabel(usize);
68-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemLabel)]
68+
#[derive(Debug, Clone, Copy, SystemLabel)]
6969
struct DummyLabel;
7070

71+
impl SystemLabel for NumLabel {
72+
fn as_str(&self) -> &'static str {
73+
let s = self.0.to_string();
74+
Box::leak(s.into_boxed_str())
75+
}
76+
}
77+
7178
let mut group = criterion.benchmark_group("build_schedule");
7279
group.warm_up_time(std::time::Duration::from_millis(500));
7380
group.measurement_time(std::time::Duration::from_secs(15));
7481

7582
// Method: generate a set of `graph_size` systems which have a One True Ordering.
7683
// Add system to the stage with full constraints. Hopefully this should be maximimally
7784
// difficult for bevy to figure out.
78-
let labels: Vec<_> = (0..1000).map(NumLabel).collect();
85+
// Also, we are performing the `as_label` operation outside of the loop since that
86+
// requires an allocation and a leak. This is not something that would be necessary in a
87+
// real scenario, just a contrivance for the benchmark.
88+
let labels: Vec<_> = (0..1000).map(|i| NumLabel(i).as_label()).collect();
7989

8090
// Benchmark graphs of different sizes.
8191
for graph_size in [100, 500, 1000] {

crates/bevy_app/src/app.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub struct App {
5656
pub runner: Box<dyn Fn(App)>,
5757
/// A container of [`Stage`]s set to be run in a linear order.
5858
pub schedule: Schedule,
59-
sub_apps: HashMap<Box<dyn AppLabel>, SubApp>,
59+
sub_apps: HashMap<AppLabelId, SubApp>,
6060
}
6161

6262
/// Each `SubApp` has its own [`Schedule`] and [`World`], enabling a separation of concerns.
@@ -879,7 +879,7 @@ impl App {
879879
sub_app_runner: impl Fn(&mut World, &mut App) + 'static,
880880
) -> &mut Self {
881881
self.sub_apps.insert(
882-
Box::new(label),
882+
label.as_label(),
883883
SubApp {
884884
app,
885885
runner: Box::new(sub_app_runner),
@@ -896,15 +896,16 @@ impl App {
896896
pub fn sub_app_mut(&mut self, label: impl AppLabel) -> &mut App {
897897
match self.get_sub_app_mut(label) {
898898
Ok(app) => app,
899-
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label),
899+
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label.as_str()),
900900
}
901901
}
902902

903903
/// Retrieves a `SubApp` inside this [`App`] with the given label, if it exists. Otherwise returns
904904
/// an [`Err`] containing the given label.
905-
pub fn get_sub_app_mut(&mut self, label: impl AppLabel) -> Result<&mut App, impl AppLabel> {
905+
pub fn get_sub_app_mut(&mut self, label: impl AppLabel) -> Result<&mut App, AppLabelId> {
906+
let label = label.as_label();
906907
self.sub_apps
907-
.get_mut((&label) as &dyn AppLabel)
908+
.get_mut(&label)
908909
.map(|sub_app| &mut sub_app.app)
909910
.ok_or(label)
910911
}
@@ -917,15 +918,15 @@ impl App {
917918
pub fn sub_app(&self, label: impl AppLabel) -> &App {
918919
match self.get_sub_app(label) {
919920
Ok(app) => app,
920-
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label),
921+
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label.as_str()),
921922
}
922923
}
923924

924925
/// Retrieves a `SubApp` inside this [`App`] with the given label, if it exists. Otherwise returns
925926
/// an [`Err`] containing the given label.
926927
pub fn get_sub_app(&self, label: impl AppLabel) -> Result<&App, impl AppLabel> {
927928
self.sub_apps
928-
.get((&label) as &dyn AppLabel)
929+
.get(&label.as_label())
929930
.map(|sub_app| &sub_app.app)
930931
.ok_or(label)
931932
}

crates/bevy_ecs/src/schedule/label.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,3 @@ define_label!(StageLabel);
55
define_label!(SystemLabel);
66
define_label!(AmbiguitySetLabel);
77
define_label!(RunCriteriaLabel);
8-
9-
pub(crate) type BoxedStageLabel = Box<dyn StageLabel>;
10-
pub(crate) type BoxedSystemLabel = Box<dyn SystemLabel>;
11-
pub(crate) type BoxedAmbiguitySetLabel = Box<dyn AmbiguitySetLabel>;
12-
pub(crate) type BoxedRunCriteriaLabel = Box<dyn RunCriteriaLabel>;

crates/bevy_ecs/src/schedule/mod.rs

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ use bevy_utils::HashMap;
3838
/// runs indefinitely.
3939
#[derive(Default)]
4040
pub struct Schedule {
41-
stages: HashMap<BoxedStageLabel, Box<dyn Stage>>,
42-
stage_order: Vec<BoxedStageLabel>,
41+
stages: HashMap<StageLabelId, Box<dyn Stage>>,
42+
stage_order: Vec<StageLabelId>,
4343
run_criteria: BoxedRunCriteria,
4444
}
4545

@@ -109,9 +109,9 @@ impl Schedule {
109109
/// schedule.add_stage("my_stage", SystemStage::parallel());
110110
/// ```
111111
pub fn add_stage<S: Stage>(&mut self, label: impl StageLabel, stage: S) -> &mut Self {
112-
let label: Box<dyn StageLabel> = Box::new(label);
113-
self.stage_order.push(label.clone());
114-
let prev = self.stages.insert(label.clone(), Box::new(stage));
112+
let label = label.as_label();
113+
self.stage_order.push(label);
114+
let prev = self.stages.insert(label, Box::new(stage));
115115
assert!(prev.is_none(), "Stage already exists: {:?}.", label);
116116
self
117117
}
@@ -133,18 +133,18 @@ impl Schedule {
133133
label: impl StageLabel,
134134
stage: S,
135135
) -> &mut Self {
136-
let label: Box<dyn StageLabel> = Box::new(label);
137-
let target = &target as &dyn StageLabel;
136+
let label = label.as_label();
137+
let target = target.as_label();
138138
let target_index = self
139139
.stage_order
140140
.iter()
141141
.enumerate()
142-
.find(|(_i, stage_label)| &***stage_label == target)
142+
.find(|(_i, stage_label)| **stage_label == target)
143143
.map(|(i, _)| i)
144144
.unwrap_or_else(|| panic!("Target stage does not exist: {:?}.", target));
145145

146-
self.stage_order.insert(target_index + 1, label.clone());
147-
let prev = self.stages.insert(label.clone(), Box::new(stage));
146+
self.stage_order.insert(target_index + 1, label);
147+
let prev = self.stages.insert(label, Box::new(stage));
148148
assert!(prev.is_none(), "Stage already exists: {:?}.", label);
149149
self
150150
}
@@ -167,18 +167,18 @@ impl Schedule {
167167
label: impl StageLabel,
168168
stage: S,
169169
) -> &mut Self {
170-
let label: Box<dyn StageLabel> = Box::new(label);
171-
let target = &target as &dyn StageLabel;
170+
let label = label.as_label();
171+
let target = target.as_label();
172172
let target_index = self
173173
.stage_order
174174
.iter()
175175
.enumerate()
176-
.find(|(_i, stage_label)| &***stage_label == target)
176+
.find(|(_i, stage_label)| **stage_label == target)
177177
.map(|(i, _)| i)
178178
.unwrap_or_else(|| panic!("Target stage does not exist: {:?}.", target));
179179

180-
self.stage_order.insert(target_index, label.clone());
181-
let prev = self.stages.insert(label.clone(), Box::new(stage));
180+
self.stage_order.insert(target_index, label);
181+
let prev = self.stages.insert(label, Box::new(stage));
182182
assert!(prev.is_none(), "Stage already exists: {:?}.", label);
183183
self
184184
}
@@ -213,7 +213,7 @@ impl Schedule {
213213

214214
let stage = self
215215
.get_stage_mut::<SystemStage>(&stage_label)
216-
.unwrap_or_else(move || stage_not_found(&stage_label));
216+
.unwrap_or_else(move || stage_not_found(&stage_label.as_label()));
217217
stage.add_system(system);
218218
self
219219
}
@@ -282,7 +282,10 @@ impl Schedule {
282282
func: F,
283283
) -> &mut Self {
284284
let stage = self.get_stage_mut::<T>(&label).unwrap_or_else(move || {
285-
panic!("stage '{:?}' does not exist or is the wrong type", label)
285+
panic!(
286+
"stage '{:?}' does not exist or is the wrong type",
287+
label.as_label()
288+
)
286289
});
287290
func(stage);
288291
self
@@ -305,7 +308,7 @@ impl Schedule {
305308
/// ```
306309
pub fn get_stage<T: Stage>(&self, label: &dyn StageLabel) -> Option<&T> {
307310
self.stages
308-
.get(label)
311+
.get(&label.as_label())
309312
.and_then(|stage| stage.downcast_ref::<T>())
310313
}
311314

@@ -326,7 +329,7 @@ impl Schedule {
326329
/// ```
327330
pub fn get_stage_mut<T: Stage>(&mut self, label: &dyn StageLabel) -> Option<&mut T> {
328331
self.stages
329-
.get_mut(label)
332+
.get_mut(&label.as_label())
330333
.and_then(|stage| stage.downcast_mut::<T>())
331334
}
332335

@@ -341,10 +344,10 @@ impl Schedule {
341344
}
342345

343346
/// Iterates over all of schedule's stages and their labels, in execution order.
344-
pub fn iter_stages(&self) -> impl Iterator<Item = (&dyn StageLabel, &dyn Stage)> {
347+
pub fn iter_stages(&self) -> impl Iterator<Item = (StageLabelId, &dyn Stage)> {
345348
self.stage_order
346349
.iter()
347-
.map(move |label| (&**label, &*self.stages[label]))
350+
.map(move |&label| (label, &*self.stages[&label]))
348351
}
349352
}
350353

crates/bevy_ecs/src/schedule/run_criteria.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
schedule::{BoxedRunCriteriaLabel, GraphNode, RunCriteriaLabel},
2+
schedule::{GraphNode, RunCriteriaLabel, RunCriteriaLabelId},
33
system::{BoxedSystem, IntoSystem, Local},
44
world::World,
55
};
@@ -104,9 +104,9 @@ pub(crate) enum RunCriteriaInner {
104104
pub(crate) struct RunCriteriaContainer {
105105
pub(crate) should_run: ShouldRun,
106106
pub(crate) inner: RunCriteriaInner,
107-
pub(crate) label: Option<BoxedRunCriteriaLabel>,
108-
pub(crate) before: Vec<BoxedRunCriteriaLabel>,
109-
pub(crate) after: Vec<BoxedRunCriteriaLabel>,
107+
pub(crate) label: Option<RunCriteriaLabelId>,
108+
pub(crate) before: Vec<RunCriteriaLabelId>,
109+
pub(crate) after: Vec<RunCriteriaLabelId>,
110110
}
111111

112112
impl RunCriteriaContainer {
@@ -139,7 +139,7 @@ impl RunCriteriaContainer {
139139
}
140140

141141
impl GraphNode for RunCriteriaContainer {
142-
type Label = BoxedRunCriteriaLabel;
142+
type Label = RunCriteriaLabelId;
143143

144144
fn name(&self) -> Cow<'static, str> {
145145
match &self.inner {
@@ -148,26 +148,26 @@ impl GraphNode for RunCriteriaContainer {
148148
}
149149
}
150150

151-
fn labels(&self) -> &[BoxedRunCriteriaLabel] {
151+
fn labels(&self) -> &[RunCriteriaLabelId] {
152152
if let Some(ref label) = self.label {
153153
std::slice::from_ref(label)
154154
} else {
155155
&[]
156156
}
157157
}
158158

159-
fn before(&self) -> &[BoxedRunCriteriaLabel] {
159+
fn before(&self) -> &[RunCriteriaLabelId] {
160160
&self.before
161161
}
162162

163-
fn after(&self) -> &[BoxedRunCriteriaLabel] {
163+
fn after(&self) -> &[RunCriteriaLabelId] {
164164
&self.after
165165
}
166166
}
167167

168168
pub enum RunCriteriaDescriptorOrLabel {
169169
Descriptor(RunCriteriaDescriptor),
170-
Label(BoxedRunCriteriaLabel),
170+
Label(RunCriteriaLabelId),
171171
}
172172

173173
#[derive(Clone, Copy)]
@@ -178,10 +178,10 @@ pub(crate) enum DuplicateLabelStrategy {
178178

179179
pub struct RunCriteriaDescriptor {
180180
pub(crate) system: RunCriteriaSystem,
181-
pub(crate) label: Option<BoxedRunCriteriaLabel>,
181+
pub(crate) label: Option<RunCriteriaLabelId>,
182182
pub(crate) duplicate_label_strategy: DuplicateLabelStrategy,
183-
pub(crate) before: Vec<BoxedRunCriteriaLabel>,
184-
pub(crate) after: Vec<BoxedRunCriteriaLabel>,
183+
pub(crate) before: Vec<RunCriteriaLabelId>,
184+
pub(crate) after: Vec<RunCriteriaLabelId>,
185185
}
186186

187187
pub(crate) enum RunCriteriaSystem {
@@ -222,12 +222,12 @@ where
222222
}
223223
}
224224

225-
impl<L> IntoRunCriteria<BoxedRunCriteriaLabel> for L
225+
impl<L> IntoRunCriteria<RunCriteriaLabelId> for L
226226
where
227227
L: RunCriteriaLabel,
228228
{
229229
fn into(self) -> RunCriteriaDescriptorOrLabel {
230-
RunCriteriaDescriptorOrLabel::Label(Box::new(self))
230+
RunCriteriaDescriptorOrLabel::Label(self.as_label())
231231
}
232232
}
233233

@@ -254,24 +254,24 @@ pub trait RunCriteriaDescriptorCoercion<Param> {
254254

255255
impl RunCriteriaDescriptorCoercion<()> for RunCriteriaDescriptor {
256256
fn label(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
257-
self.label = Some(Box::new(label));
257+
self.label = Some(label.as_label());
258258
self.duplicate_label_strategy = DuplicateLabelStrategy::Panic;
259259
self
260260
}
261261

262262
fn label_discard_if_duplicate(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
263-
self.label = Some(Box::new(label));
263+
self.label = Some(label.as_label());
264264
self.duplicate_label_strategy = DuplicateLabelStrategy::Discard;
265265
self
266266
}
267267

268268
fn before(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
269-
self.before.push(Box::new(label));
269+
self.before.push(label.as_label());
270270
self
271271
}
272272

273273
fn after(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
274-
self.after.push(Box::new(label));
274+
self.after.push(label.as_label());
275275
self
276276
}
277277
}
@@ -327,7 +327,7 @@ where
327327
}
328328

329329
pub struct RunCriteria {
330-
label: BoxedRunCriteriaLabel,
330+
label: RunCriteriaLabelId,
331331
}
332332

333333
impl RunCriteria {
@@ -342,7 +342,7 @@ impl RunCriteria {
342342
label: None,
343343
duplicate_label_strategy: DuplicateLabelStrategy::Panic,
344344
before: vec![],
345-
after: vec![Box::new(label)],
345+
after: vec![label.as_label()],
346346
}
347347
}
348348
}

0 commit comments

Comments
 (0)