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.
This commit is contained in:
JoJoJet 2022-07-14 18:23:01 +00:00
parent 234e5af882
commit c43295af80
15 changed files with 309 additions and 318 deletions

View file

@ -63,11 +63,18 @@ pub fn build_schedule(criterion: &mut Criterion) {
// Use multiple different kinds of label to ensure that dynamic dispatch
// doesn't somehow get optimized away.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemLabel)]
#[derive(Debug, Clone, Copy)]
struct NumLabel(usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemLabel)]
#[derive(Debug, Clone, Copy, SystemLabel)]
struct DummyLabel;
impl SystemLabel for NumLabel {
fn as_str(&self) -> &'static str {
let s = self.0.to_string();
Box::leak(s.into_boxed_str())
}
}
let mut group = criterion.benchmark_group("build_schedule");
group.warm_up_time(std::time::Duration::from_millis(500));
group.measurement_time(std::time::Duration::from_secs(15));
@ -75,7 +82,10 @@ pub fn build_schedule(criterion: &mut Criterion) {
// Method: generate a set of `graph_size` systems which have a One True Ordering.
// Add system to the stage with full constraints. Hopefully this should be maximimally
// difficult for bevy to figure out.
let labels: Vec<_> = (0..1000).map(NumLabel).collect();
// Also, we are performing the `as_label` operation outside of the loop since that
// requires an allocation and a leak. This is not something that would be necessary in a
// real scenario, just a contrivance for the benchmark.
let labels: Vec<_> = (0..1000).map(|i| NumLabel(i).as_label()).collect();
// Benchmark graphs of different sizes.
for graph_size in [100, 500, 1000] {

View file

@ -56,7 +56,7 @@ pub struct App {
pub runner: Box<dyn Fn(App)>,
/// A container of [`Stage`]s set to be run in a linear order.
pub schedule: Schedule,
sub_apps: HashMap<Box<dyn AppLabel>, SubApp>,
sub_apps: HashMap<AppLabelId, SubApp>,
}
/// Each `SubApp` has its own [`Schedule`] and [`World`], enabling a separation of concerns.
@ -879,7 +879,7 @@ impl App {
sub_app_runner: impl Fn(&mut World, &mut App) + 'static,
) -> &mut Self {
self.sub_apps.insert(
Box::new(label),
label.as_label(),
SubApp {
app,
runner: Box::new(sub_app_runner),
@ -896,15 +896,16 @@ 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),
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label.as_str()),
}
}
/// 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, impl AppLabel> {
pub fn get_sub_app_mut(&mut self, label: impl AppLabel) -> Result<&mut App, AppLabelId> {
let label = label.as_label();
self.sub_apps
.get_mut((&label) as &dyn AppLabel)
.get_mut(&label)
.map(|sub_app| &mut sub_app.app)
.ok_or(label)
}
@ -917,7 +918,7 @@ 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),
Err(label) => panic!("Sub-App with label '{:?}' does not exist", label.as_str()),
}
}
@ -925,7 +926,7 @@ impl App {
/// 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 &dyn AppLabel)
.get(&label.as_label())
.map(|sub_app| &sub_app.app)
.ok_or(label)
}

View file

@ -5,8 +5,3 @@ define_label!(StageLabel);
define_label!(SystemLabel);
define_label!(AmbiguitySetLabel);
define_label!(RunCriteriaLabel);
pub(crate) type BoxedStageLabel = Box<dyn StageLabel>;
pub(crate) type BoxedSystemLabel = Box<dyn SystemLabel>;
pub(crate) type BoxedAmbiguitySetLabel = Box<dyn AmbiguitySetLabel>;
pub(crate) type BoxedRunCriteriaLabel = Box<dyn RunCriteriaLabel>;

View file

@ -38,8 +38,8 @@ use bevy_utils::HashMap;
/// runs indefinitely.
#[derive(Default)]
pub struct Schedule {
stages: HashMap<BoxedStageLabel, Box<dyn Stage>>,
stage_order: Vec<BoxedStageLabel>,
stages: HashMap<StageLabelId, Box<dyn Stage>>,
stage_order: Vec<StageLabelId>,
run_criteria: BoxedRunCriteria,
}
@ -109,9 +109,9 @@ impl Schedule {
/// schedule.add_stage("my_stage", SystemStage::parallel());
/// ```
pub fn add_stage<S: Stage>(&mut self, label: impl StageLabel, stage: S) -> &mut Self {
let label: Box<dyn StageLabel> = Box::new(label);
self.stage_order.push(label.clone());
let prev = self.stages.insert(label.clone(), Box::new(stage));
let label = label.as_label();
self.stage_order.push(label);
let prev = self.stages.insert(label, Box::new(stage));
assert!(prev.is_none(), "Stage already exists: {:?}.", label);
self
}
@ -133,18 +133,18 @@ impl Schedule {
label: impl StageLabel,
stage: S,
) -> &mut Self {
let label: Box<dyn StageLabel> = Box::new(label);
let target = &target as &dyn StageLabel;
let label = label.as_label();
let target = target.as_label();
let target_index = self
.stage_order
.iter()
.enumerate()
.find(|(_i, stage_label)| &***stage_label == target)
.find(|(_i, stage_label)| **stage_label == target)
.map(|(i, _)| i)
.unwrap_or_else(|| panic!("Target stage does not exist: {:?}.", target));
self.stage_order.insert(target_index + 1, label.clone());
let prev = self.stages.insert(label.clone(), Box::new(stage));
self.stage_order.insert(target_index + 1, label);
let prev = self.stages.insert(label, Box::new(stage));
assert!(prev.is_none(), "Stage already exists: {:?}.", label);
self
}
@ -167,18 +167,18 @@ impl Schedule {
label: impl StageLabel,
stage: S,
) -> &mut Self {
let label: Box<dyn StageLabel> = Box::new(label);
let target = &target as &dyn StageLabel;
let label = label.as_label();
let target = target.as_label();
let target_index = self
.stage_order
.iter()
.enumerate()
.find(|(_i, stage_label)| &***stage_label == target)
.find(|(_i, stage_label)| **stage_label == target)
.map(|(i, _)| i)
.unwrap_or_else(|| panic!("Target stage does not exist: {:?}.", target));
self.stage_order.insert(target_index, label.clone());
let prev = self.stages.insert(label.clone(), Box::new(stage));
self.stage_order.insert(target_index, label);
let prev = self.stages.insert(label, Box::new(stage));
assert!(prev.is_none(), "Stage already exists: {:?}.", label);
self
}
@ -213,7 +213,7 @@ impl Schedule {
let stage = self
.get_stage_mut::<SystemStage>(&stage_label)
.unwrap_or_else(move || stage_not_found(&stage_label));
.unwrap_or_else(move || stage_not_found(&stage_label.as_label()));
stage.add_system(system);
self
}
@ -282,7 +282,10 @@ impl Schedule {
func: F,
) -> &mut Self {
let stage = self.get_stage_mut::<T>(&label).unwrap_or_else(move || {
panic!("stage '{:?}' does not exist or is the wrong type", label)
panic!(
"stage '{:?}' does not exist or is the wrong type",
label.as_label()
)
});
func(stage);
self
@ -305,7 +308,7 @@ impl Schedule {
/// ```
pub fn get_stage<T: Stage>(&self, label: &dyn StageLabel) -> Option<&T> {
self.stages
.get(label)
.get(&label.as_label())
.and_then(|stage| stage.downcast_ref::<T>())
}
@ -326,7 +329,7 @@ impl Schedule {
/// ```
pub fn get_stage_mut<T: Stage>(&mut self, label: &dyn StageLabel) -> Option<&mut T> {
self.stages
.get_mut(label)
.get_mut(&label.as_label())
.and_then(|stage| stage.downcast_mut::<T>())
}
@ -341,10 +344,10 @@ impl Schedule {
}
/// Iterates over all of schedule's stages and their labels, in execution order.
pub fn iter_stages(&self) -> impl Iterator<Item = (&dyn StageLabel, &dyn Stage)> {
pub fn iter_stages(&self) -> impl Iterator<Item = (StageLabelId, &dyn Stage)> {
self.stage_order
.iter()
.map(move |label| (&**label, &*self.stages[label]))
.map(move |&label| (label, &*self.stages[&label]))
}
}

View file

@ -1,5 +1,5 @@
use crate::{
schedule::{BoxedRunCriteriaLabel, GraphNode, RunCriteriaLabel},
schedule::{GraphNode, RunCriteriaLabel, RunCriteriaLabelId},
system::{BoxedSystem, IntoSystem, Local},
world::World,
};
@ -104,9 +104,9 @@ pub(crate) enum RunCriteriaInner {
pub(crate) struct RunCriteriaContainer {
pub(crate) should_run: ShouldRun,
pub(crate) inner: RunCriteriaInner,
pub(crate) label: Option<BoxedRunCriteriaLabel>,
pub(crate) before: Vec<BoxedRunCriteriaLabel>,
pub(crate) after: Vec<BoxedRunCriteriaLabel>,
pub(crate) label: Option<RunCriteriaLabelId>,
pub(crate) before: Vec<RunCriteriaLabelId>,
pub(crate) after: Vec<RunCriteriaLabelId>,
}
impl RunCriteriaContainer {
@ -139,7 +139,7 @@ impl RunCriteriaContainer {
}
impl GraphNode for RunCriteriaContainer {
type Label = BoxedRunCriteriaLabel;
type Label = RunCriteriaLabelId;
fn name(&self) -> Cow<'static, str> {
match &self.inner {
@ -148,7 +148,7 @@ impl GraphNode for RunCriteriaContainer {
}
}
fn labels(&self) -> &[BoxedRunCriteriaLabel] {
fn labels(&self) -> &[RunCriteriaLabelId] {
if let Some(ref label) = self.label {
std::slice::from_ref(label)
} else {
@ -156,18 +156,18 @@ impl GraphNode for RunCriteriaContainer {
}
}
fn before(&self) -> &[BoxedRunCriteriaLabel] {
fn before(&self) -> &[RunCriteriaLabelId] {
&self.before
}
fn after(&self) -> &[BoxedRunCriteriaLabel] {
fn after(&self) -> &[RunCriteriaLabelId] {
&self.after
}
}
pub enum RunCriteriaDescriptorOrLabel {
Descriptor(RunCriteriaDescriptor),
Label(BoxedRunCriteriaLabel),
Label(RunCriteriaLabelId),
}
#[derive(Clone, Copy)]
@ -178,10 +178,10 @@ pub(crate) enum DuplicateLabelStrategy {
pub struct RunCriteriaDescriptor {
pub(crate) system: RunCriteriaSystem,
pub(crate) label: Option<BoxedRunCriteriaLabel>,
pub(crate) label: Option<RunCriteriaLabelId>,
pub(crate) duplicate_label_strategy: DuplicateLabelStrategy,
pub(crate) before: Vec<BoxedRunCriteriaLabel>,
pub(crate) after: Vec<BoxedRunCriteriaLabel>,
pub(crate) before: Vec<RunCriteriaLabelId>,
pub(crate) after: Vec<RunCriteriaLabelId>,
}
pub(crate) enum RunCriteriaSystem {
@ -222,12 +222,12 @@ where
}
}
impl<L> IntoRunCriteria<BoxedRunCriteriaLabel> for L
impl<L> IntoRunCriteria<RunCriteriaLabelId> for L
where
L: RunCriteriaLabel,
{
fn into(self) -> RunCriteriaDescriptorOrLabel {
RunCriteriaDescriptorOrLabel::Label(Box::new(self))
RunCriteriaDescriptorOrLabel::Label(self.as_label())
}
}
@ -254,24 +254,24 @@ pub trait RunCriteriaDescriptorCoercion<Param> {
impl RunCriteriaDescriptorCoercion<()> for RunCriteriaDescriptor {
fn label(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
self.label = Some(Box::new(label));
self.label = Some(label.as_label());
self.duplicate_label_strategy = DuplicateLabelStrategy::Panic;
self
}
fn label_discard_if_duplicate(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
self.label = Some(Box::new(label));
self.label = Some(label.as_label());
self.duplicate_label_strategy = DuplicateLabelStrategy::Discard;
self
}
fn before(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
self.before.push(Box::new(label));
self.before.push(label.as_label());
self
}
fn after(mut self, label: impl RunCriteriaLabel) -> RunCriteriaDescriptor {
self.after.push(Box::new(label));
self.after.push(label.as_label());
self
}
}
@ -327,7 +327,7 @@ where
}
pub struct RunCriteria {
label: BoxedRunCriteriaLabel,
label: RunCriteriaLabelId,
}
impl RunCriteria {
@ -342,7 +342,7 @@ impl RunCriteria {
label: None,
duplicate_label_strategy: DuplicateLabelStrategy::Panic,
before: vec![],
after: vec![Box::new(label)],
after: vec![label.as_label()],
}
}
}

View file

@ -4,11 +4,11 @@ use crate::{
prelude::IntoSystem,
schedule::{
graph_utils::{self, DependencyGraphError},
BoxedRunCriteria, BoxedRunCriteriaLabel, BoxedSystemLabel, DuplicateLabelStrategy,
ExclusiveSystemContainer, GraphNode, InsertionPoint, ParallelExecutor,
ParallelSystemContainer, ParallelSystemExecutor, RunCriteriaContainer,
RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel, RunCriteriaInner, ShouldRun,
SingleThreadedExecutor, SystemContainer, SystemDescriptor, SystemSet,
BoxedRunCriteria, DuplicateLabelStrategy, ExclusiveSystemContainer, GraphNode,
InsertionPoint, ParallelExecutor, ParallelSystemContainer, ParallelSystemExecutor,
RunCriteriaContainer, RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel,
RunCriteriaInner, RunCriteriaLabelId, ShouldRun, SingleThreadedExecutor, SystemContainer,
SystemDescriptor, SystemLabelId, SystemSet,
},
world::{World, WorldId},
};
@ -171,7 +171,7 @@ impl SystemStage {
container.run_criteria_label = Some(label);
}
Some(RunCriteriaDescriptorOrLabel::Descriptor(criteria_descriptor)) => {
container.run_criteria_label = criteria_descriptor.label.clone();
container.run_criteria_label = criteria_descriptor.label;
container.run_criteria_index =
Some(self.add_run_criteria_internal(criteria_descriptor));
}
@ -205,7 +205,7 @@ impl SystemStage {
container.run_criteria_label = Some(label);
}
Some(RunCriteriaDescriptorOrLabel::Descriptor(criteria_descriptor)) => {
container.run_criteria_label = criteria_descriptor.label.clone();
container.run_criteria_label = criteria_descriptor.label;
container.run_criteria_index =
Some(self.add_run_criteria_internal(criteria_descriptor));
}
@ -301,11 +301,11 @@ impl SystemStage {
match system {
SystemDescriptor::Exclusive(descriptor) => {
descriptor.run_criteria =
Some(RunCriteriaDescriptorOrLabel::Label(label.clone()));
Some(RunCriteriaDescriptorOrLabel::Label(label));
}
SystemDescriptor::Parallel(descriptor) => {
descriptor.run_criteria =
Some(RunCriteriaDescriptorOrLabel::Label(label.clone()));
Some(RunCriteriaDescriptorOrLabel::Label(label));
}
}
}
@ -372,7 +372,7 @@ impl SystemStage {
.enumerate()
.filter_map(|(index, mut container)| {
let new_index = index - filtered_criteria;
let label = container.label.clone();
let label = container.label;
if let Some(strategy) = uninitialized_criteria.get(&index) {
if let Some(ref label) = label {
if let Some(duplicate_index) = criteria_labels.get(label) {
@ -621,10 +621,8 @@ impl SystemStage {
/// Returns a map of run criteria labels to their indices.
fn process_run_criteria(
&mut self,
) -> Result<
HashMap<BoxedRunCriteriaLabel, usize>,
DependencyGraphError<HashSet<BoxedRunCriteriaLabel>>,
> {
) -> Result<HashMap<RunCriteriaLabelId, usize>, DependencyGraphError<HashSet<RunCriteriaLabelId>>>
{
let graph = graph_utils::build_dependency_graph(&self.run_criteria);
let order = graph_utils::topological_order(&graph)?;
let mut order_inverted = order.iter().enumerate().collect::<Vec<_>>();
@ -637,7 +635,7 @@ impl SystemStage {
criteria
.label
.as_ref()
.map(|label| (label.clone(), order_inverted[index].0))
.map(|&label| (label, order_inverted[index].0))
})
.collect();
for criteria in &mut self.run_criteria {
@ -680,8 +678,8 @@ impl SystemStage {
/// and run criteria.
fn process_systems(
systems: &mut Vec<impl SystemContainer>,
run_criteria_labels: &HashMap<BoxedRunCriteriaLabel, usize>,
) -> Result<(), DependencyGraphError<HashSet<BoxedSystemLabel>>> {
run_criteria_labels: &HashMap<RunCriteriaLabelId, usize>,
) -> Result<(), DependencyGraphError<HashSet<SystemLabelId>>> {
let mut graph = graph_utils::build_dependency_graph(systems);
let order = graph_utils::topological_order(&graph)?;
let mut order_inverted = order.iter().enumerate().collect::<Vec<_>>();
@ -974,9 +972,9 @@ impl Stage for SystemStage {
mod tests {
use crate::{
schedule::{
BoxedSystemLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion,
RunCriteria, RunCriteriaDescriptorCoercion, ShouldRun, SingleThreadedExecutor, Stage,
SystemSet, SystemStage,
ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, RunCriteria,
RunCriteriaDescriptorCoercion, ShouldRun, SingleThreadedExecutor, Stage, SystemLabel,
SystemLabelId, SystemSet, SystemStage,
},
system::{In, IntoExclusiveSystem, Local, Query, ResMut},
world::World,
@ -1609,23 +1607,21 @@ mod tests {
fn find_ambiguities_first_str_labels(
systems: &[impl SystemContainer],
) -> Vec<(BoxedSystemLabel, BoxedSystemLabel)> {
) -> Vec<(SystemLabelId, SystemLabelId)> {
find_ambiguities(systems)
.drain(..)
.map(|(index_a, index_b, _conflicts)| {
(
systems[index_a]
*systems[index_a]
.labels()
.iter()
.find(|a| (&***a).type_id() == std::any::TypeId::of::<&str>())
.unwrap()
.clone(),
systems[index_b]
.find(|a| a.type_id() == std::any::TypeId::of::<&str>())
.unwrap(),
*systems[index_b]
.labels()
.iter()
.find(|a| (&***a).type_id() == std::any::TypeId::of::<&str>())
.unwrap()
.clone(),
.find(|a| a.type_id() == std::any::TypeId::of::<&str>())
.unwrap(),
)
})
.collect()
@ -1657,8 +1653,8 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "1".as_label()))
);
assert_eq!(ambiguities.len(), 1);
@ -1672,8 +1668,8 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "1".as_label()))
);
assert_eq!(ambiguities.len(), 1);
@ -1697,12 +1693,12 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!(
ambiguities.contains(&(Box::new("0"), Box::new("3")))
|| ambiguities.contains(&(Box::new("3"), Box::new("0")))
ambiguities.contains(&("0".as_label(), "3".as_label()))
|| ambiguities.contains(&("3".as_label(), "0".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "1".as_label()))
);
assert_eq!(ambiguities.len(), 2);
@ -1716,8 +1712,8 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!(
ambiguities.contains(&(Box::new("0"), Box::new("3")))
|| ambiguities.contains(&(Box::new("3"), Box::new("0")))
ambiguities.contains(&("0".as_label(), "3".as_label()))
|| ambiguities.contains(&("3".as_label(), "0".as_label()))
);
assert_eq!(ambiguities.len(), 1);
@ -1729,8 +1725,8 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!(
ambiguities.contains(&(Box::new("0"), Box::new("1")))
|| ambiguities.contains(&(Box::new("1"), Box::new("0")))
ambiguities.contains(&("0".as_label(), "1".as_label()))
|| ambiguities.contains(&("1".as_label(), "0".as_label()))
);
assert_eq!(ambiguities.len(), 1);
@ -1742,8 +1738,8 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("2")))
|| ambiguities.contains(&(Box::new("2"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "2".as_label()))
|| ambiguities.contains(&("2".as_label(), "1".as_label()))
);
assert_eq!(ambiguities.len(), 1);
@ -1756,8 +1752,8 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("2")))
|| ambiguities.contains(&(Box::new("2"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "2".as_label()))
|| ambiguities.contains(&("2".as_label(), "1".as_label()))
);
assert_eq!(ambiguities.len(), 1);
@ -1780,8 +1776,8 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("2")))
|| ambiguities.contains(&(Box::new("2"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "2".as_label()))
|| ambiguities.contains(&("2".as_label(), "1".as_label()))
);
assert_eq!(ambiguities.len(), 1);
@ -1810,28 +1806,28 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("2")))
|| ambiguities.contains(&(Box::new("2"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "2".as_label()))
|| ambiguities.contains(&("2".as_label(), "1".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("3")))
|| ambiguities.contains(&(Box::new("3"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "3".as_label()))
|| ambiguities.contains(&("3".as_label(), "1".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "1".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("2"), Box::new("3")))
|| ambiguities.contains(&(Box::new("3"), Box::new("2")))
ambiguities.contains(&("2".as_label(), "3".as_label()))
|| ambiguities.contains(&("3".as_label(), "2".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("2"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("2")))
ambiguities.contains(&("2".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "2".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("3"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("3")))
ambiguities.contains(&("3".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "3".as_label()))
);
assert_eq!(ambiguities.len(), 6);
@ -1891,12 +1887,12 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.parallel);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "1".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("2"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("2")))
ambiguities.contains(&("2".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "2".as_label()))
);
assert_eq!(ambiguities.len(), 2);
@ -1925,28 +1921,28 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.exclusive_at_start);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("3")))
|| ambiguities.contains(&(Box::new("3"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "3".as_label()))
|| ambiguities.contains(&("3".as_label(), "1".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("2"), Box::new("3")))
|| ambiguities.contains(&(Box::new("3"), Box::new("2")))
ambiguities.contains(&("2".as_label(), "3".as_label()))
|| ambiguities.contains(&("3".as_label(), "2".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "1".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("2"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("2")))
ambiguities.contains(&("2".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "2".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("5")))
|| ambiguities.contains(&(Box::new("5"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "5".as_label()))
|| ambiguities.contains(&("5".as_label(), "1".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("2"), Box::new("5")))
|| ambiguities.contains(&(Box::new("5"), Box::new("2")))
ambiguities.contains(&("2".as_label(), "5".as_label()))
|| ambiguities.contains(&("5".as_label(), "2".as_label()))
);
assert_eq!(ambiguities.len(), 6);
@ -1962,20 +1958,20 @@ mod tests {
stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_str_labels(&stage.exclusive_at_start);
assert!(
ambiguities.contains(&(Box::new("2"), Box::new("3")))
|| ambiguities.contains(&(Box::new("3"), Box::new("2")))
ambiguities.contains(&("2".as_label(), "3".as_label()))
|| ambiguities.contains(&("3".as_label(), "2".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("1"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("1")))
ambiguities.contains(&("1".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "1".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("2"), Box::new("4")))
|| ambiguities.contains(&(Box::new("4"), Box::new("2")))
ambiguities.contains(&("2".as_label(), "4".as_label()))
|| ambiguities.contains(&("4".as_label(), "2".as_label()))
);
assert!(
ambiguities.contains(&(Box::new("2"), Box::new("5")))
|| ambiguities.contains(&(Box::new("5"), Box::new("2")))
ambiguities.contains(&("2".as_label(), "5".as_label()))
|| ambiguities.contains(&("5".as_label(), "2".as_label()))
);
assert_eq!(ambiguities.len(), 4);

View file

@ -53,47 +53,19 @@ enum ScheduledOperation<T: StateData> {
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
enum StateCallback {
Update,
InactiveUpdate,
InStackUpdate,
Enter,
Exit,
Pause,
Resume,
}
impl StateCallback {
fn into_label<T>(self, state: T) -> StateRunCriteriaLabel<T>
where
T: StateData,
{
StateRunCriteriaLabel(state, self)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
struct StateRunCriteriaLabel<T>(T, StateCallback);
impl<T> RunCriteriaLabel for StateRunCriteriaLabel<T>
where
T: StateData,
{
fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel> {
Box::new(self.clone())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
struct DriverLabel(TypeId);
struct DriverLabel(TypeId, &'static str);
impl RunCriteriaLabel for DriverLabel {
fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel> {
Box::new(self.clone())
fn type_id(&self) -> core::any::TypeId {
self.0
}
fn as_str(&self) -> &'static str {
self.1
}
}
impl DriverLabel {
fn of<T: 'static>() -> Self {
Self(TypeId::of::<T>())
Self(TypeId::of::<T>(), std::any::type_name::<T>())
}
}
@ -102,17 +74,14 @@ where
T: StateData,
{
pub fn on_update(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>| {
state.stack.last().unwrap() == &pred && state.transition.is_none()
})
.chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Update.into_label(pred_clone))
}
pub fn on_inactive_update(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>, mut is_inactive: Local<bool>| match &state.transition {
Some(StateTransition::Pausing(ref relevant, _))
| Some(StateTransition::Resuming(_, ref relevant)) => {
@ -126,11 +95,9 @@ where
})
.chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::InactiveUpdate.into_label(pred_clone))
}
pub fn on_in_stack_update(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>, mut is_in_stack: Local<bool>| match &state.transition {
Some(StateTransition::Entering(ref relevant, _))
| Some(StateTransition::ExitingToResume(_, ref relevant))
@ -151,11 +118,9 @@ where
})
.chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::InStackUpdate.into_label(pred_clone))
}
pub fn on_enter(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>| {
state
.transition
@ -168,11 +133,9 @@ where
})
.chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Enter.into_label(pred_clone))
}
pub fn on_exit(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>| {
state
.transition
@ -185,11 +148,9 @@ where
})
.chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Exit.into_label(pred_clone))
}
pub fn on_pause(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>| {
state
.transition
@ -201,11 +162,9 @@ where
})
.chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Pause.into_label(pred_clone))
}
pub fn on_resume(pred: T) -> RunCriteriaDescriptor {
let pred_clone = pred.clone();
(move |state: Res<State<T>>| {
state
.transition
@ -217,7 +176,6 @@ where
})
.chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Resume.into_label(pred_clone))
}
pub fn on_update_set(s: T) -> SystemSet {

View file

@ -2,15 +2,15 @@ use crate::{
component::ComponentId,
query::Access,
schedule::{
BoxedAmbiguitySetLabel, BoxedRunCriteriaLabel, BoxedSystemLabel, ExclusiveSystemDescriptor,
GraphNode, ParallelSystemDescriptor,
AmbiguitySetLabelId, ExclusiveSystemDescriptor, GraphNode, ParallelSystemDescriptor,
RunCriteriaLabelId, SystemLabelId,
},
system::{ExclusiveSystem, System},
};
use std::borrow::Cow;
/// System metadata like its name, labels, order requirements and component access.
pub trait SystemContainer: GraphNode<Label = BoxedSystemLabel> {
pub trait SystemContainer: GraphNode<Label = SystemLabelId> {
#[doc(hidden)]
fn dependencies(&self) -> &[usize];
#[doc(hidden)]
@ -19,20 +19,20 @@ pub trait SystemContainer: GraphNode<Label = BoxedSystemLabel> {
fn run_criteria(&self) -> Option<usize>;
#[doc(hidden)]
fn set_run_criteria(&mut self, index: usize);
fn run_criteria_label(&self) -> Option<&BoxedRunCriteriaLabel>;
fn ambiguity_sets(&self) -> &[BoxedAmbiguitySetLabel];
fn run_criteria_label(&self) -> Option<&RunCriteriaLabelId>;
fn ambiguity_sets(&self) -> &[AmbiguitySetLabelId];
fn component_access(&self) -> Option<&Access<ComponentId>>;
}
pub(super) struct ExclusiveSystemContainer {
system: Box<dyn ExclusiveSystem>,
pub(super) run_criteria_index: Option<usize>,
pub(super) run_criteria_label: Option<BoxedRunCriteriaLabel>,
pub(super) run_criteria_label: Option<RunCriteriaLabelId>,
dependencies: Vec<usize>,
labels: Vec<BoxedSystemLabel>,
before: Vec<BoxedSystemLabel>,
after: Vec<BoxedSystemLabel>,
ambiguity_sets: Vec<BoxedAmbiguitySetLabel>,
labels: Vec<SystemLabelId>,
before: Vec<SystemLabelId>,
after: Vec<SystemLabelId>,
ambiguity_sets: Vec<AmbiguitySetLabelId>,
}
impl ExclusiveSystemContainer {
@ -55,21 +55,21 @@ impl ExclusiveSystemContainer {
}
impl GraphNode for ExclusiveSystemContainer {
type Label = BoxedSystemLabel;
type Label = SystemLabelId;
fn name(&self) -> Cow<'static, str> {
self.system.name()
}
fn labels(&self) -> &[BoxedSystemLabel] {
fn labels(&self) -> &[SystemLabelId] {
&self.labels
}
fn before(&self) -> &[BoxedSystemLabel] {
fn before(&self) -> &[SystemLabelId] {
&self.before
}
fn after(&self) -> &[BoxedSystemLabel] {
fn after(&self) -> &[SystemLabelId] {
&self.after
}
}
@ -92,11 +92,11 @@ impl SystemContainer for ExclusiveSystemContainer {
self.run_criteria_index = Some(index);
}
fn run_criteria_label(&self) -> Option<&BoxedRunCriteriaLabel> {
fn run_criteria_label(&self) -> Option<&RunCriteriaLabelId> {
self.run_criteria_label.as_ref()
}
fn ambiguity_sets(&self) -> &[BoxedAmbiguitySetLabel] {
fn ambiguity_sets(&self) -> &[AmbiguitySetLabelId] {
&self.ambiguity_sets
}
@ -108,13 +108,13 @@ impl SystemContainer for ExclusiveSystemContainer {
pub struct ParallelSystemContainer {
system: Box<dyn System<In = (), Out = ()>>,
pub(crate) run_criteria_index: Option<usize>,
pub(crate) run_criteria_label: Option<BoxedRunCriteriaLabel>,
pub(crate) run_criteria_label: Option<RunCriteriaLabelId>,
pub(crate) should_run: bool,
dependencies: Vec<usize>,
labels: Vec<BoxedSystemLabel>,
before: Vec<BoxedSystemLabel>,
after: Vec<BoxedSystemLabel>,
ambiguity_sets: Vec<BoxedAmbiguitySetLabel>,
labels: Vec<SystemLabelId>,
before: Vec<SystemLabelId>,
after: Vec<SystemLabelId>,
ambiguity_sets: Vec<AmbiguitySetLabelId>,
}
impl ParallelSystemContainer {
@ -154,21 +154,21 @@ impl ParallelSystemContainer {
}
impl GraphNode for ParallelSystemContainer {
type Label = BoxedSystemLabel;
type Label = SystemLabelId;
fn name(&self) -> Cow<'static, str> {
self.system().name()
}
fn labels(&self) -> &[BoxedSystemLabel] {
fn labels(&self) -> &[SystemLabelId] {
&self.labels
}
fn before(&self) -> &[BoxedSystemLabel] {
fn before(&self) -> &[SystemLabelId] {
&self.before
}
fn after(&self) -> &[BoxedSystemLabel] {
fn after(&self) -> &[SystemLabelId] {
&self.after
}
}
@ -191,11 +191,11 @@ impl SystemContainer for ParallelSystemContainer {
self.run_criteria_index = Some(index);
}
fn run_criteria_label(&self) -> Option<&BoxedRunCriteriaLabel> {
fn run_criteria_label(&self) -> Option<&RunCriteriaLabelId> {
self.run_criteria_label.as_ref()
}
fn ambiguity_sets(&self) -> &[BoxedAmbiguitySetLabel] {
fn ambiguity_sets(&self) -> &[AmbiguitySetLabelId] {
&self.ambiguity_sets
}

View file

@ -1,7 +1,7 @@
use crate::{
schedule::{
AmbiguitySetLabel, BoxedAmbiguitySetLabel, BoxedSystemLabel, IntoRunCriteria,
RunCriteriaDescriptorOrLabel, SystemLabel,
AmbiguitySetLabel, AmbiguitySetLabelId, IntoRunCriteria, RunCriteriaDescriptorOrLabel,
SystemLabel, SystemLabelId,
},
system::{
AsSystemLabel, BoxedSystem, ExclusiveSystem, ExclusiveSystemCoerced, ExclusiveSystemFn,
@ -98,10 +98,10 @@ impl IntoSystemDescriptor<()> for ExclusiveSystemCoerced {
pub struct ParallelSystemDescriptor {
pub(crate) system: BoxedSystem<(), ()>,
pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>,
pub(crate) labels: Vec<BoxedSystemLabel>,
pub(crate) before: Vec<BoxedSystemLabel>,
pub(crate) after: Vec<BoxedSystemLabel>,
pub(crate) ambiguity_sets: Vec<BoxedAmbiguitySetLabel>,
pub(crate) labels: Vec<SystemLabelId>,
pub(crate) before: Vec<SystemLabelId>,
pub(crate) after: Vec<SystemLabelId>,
pub(crate) ambiguity_sets: Vec<AmbiguitySetLabelId>,
}
fn new_parallel_descriptor(system: BoxedSystem<(), ()>) -> ParallelSystemDescriptor {
@ -147,22 +147,22 @@ impl ParallelSystemDescriptorCoercion<()> for ParallelSystemDescriptor {
}
fn label(mut self, label: impl SystemLabel) -> ParallelSystemDescriptor {
self.labels.push(Box::new(label));
self.labels.push(label.as_label());
self
}
fn before<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor {
self.before.push(Box::new(label.as_system_label()));
self.before.push(label.as_system_label().as_label());
self
}
fn after<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> ParallelSystemDescriptor {
self.after.push(Box::new(label.as_system_label()));
self.after.push(label.as_system_label().as_label());
self
}
fn in_ambiguity_set(mut self, set: impl AmbiguitySetLabel) -> ParallelSystemDescriptor {
self.ambiguity_sets.push(Box::new(set));
self.ambiguity_sets.push(set.as_label());
self
}
}
@ -232,10 +232,10 @@ pub(crate) enum InsertionPoint {
pub struct ExclusiveSystemDescriptor {
pub(crate) system: Box<dyn ExclusiveSystem>,
pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>,
pub(crate) labels: Vec<BoxedSystemLabel>,
pub(crate) before: Vec<BoxedSystemLabel>,
pub(crate) after: Vec<BoxedSystemLabel>,
pub(crate) ambiguity_sets: Vec<BoxedAmbiguitySetLabel>,
pub(crate) labels: Vec<SystemLabelId>,
pub(crate) before: Vec<SystemLabelId>,
pub(crate) after: Vec<SystemLabelId>,
pub(crate) ambiguity_sets: Vec<AmbiguitySetLabelId>,
pub(crate) insertion_point: InsertionPoint,
}
@ -293,22 +293,22 @@ impl ExclusiveSystemDescriptorCoercion for ExclusiveSystemDescriptor {
}
fn label(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
self.labels.push(Box::new(label));
self.labels.push(label.as_label());
self
}
fn before(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
self.before.push(Box::new(label));
self.before.push(label.as_label());
self
}
fn after(mut self, label: impl SystemLabel) -> ExclusiveSystemDescriptor {
self.after.push(Box::new(label));
self.after.push(label.as_label());
self
}
fn in_ambiguity_set(mut self, set: impl AmbiguitySetLabel) -> ExclusiveSystemDescriptor {
self.ambiguity_sets.push(Box::new(set));
self.ambiguity_sets.push(set.as_label());
self
}

View file

@ -1,7 +1,6 @@
use crate::schedule::{
AmbiguitySetLabel, BoxedAmbiguitySetLabel, BoxedSystemLabel, IntoRunCriteria,
IntoSystemDescriptor, RunCriteriaDescriptorOrLabel, State, StateData, SystemDescriptor,
SystemLabel,
AmbiguitySetLabel, AmbiguitySetLabelId, IntoRunCriteria, IntoSystemDescriptor,
RunCriteriaDescriptorOrLabel, State, StateData, SystemDescriptor, SystemLabel, SystemLabelId,
};
use crate::system::AsSystemLabel;
@ -10,10 +9,10 @@ use crate::system::AsSystemLabel;
pub struct SystemSet {
pub(crate) systems: Vec<SystemDescriptor>,
pub(crate) run_criteria: Option<RunCriteriaDescriptorOrLabel>,
pub(crate) labels: Vec<BoxedSystemLabel>,
pub(crate) before: Vec<BoxedSystemLabel>,
pub(crate) after: Vec<BoxedSystemLabel>,
pub(crate) ambiguity_sets: Vec<BoxedAmbiguitySetLabel>,
pub(crate) labels: Vec<SystemLabelId>,
pub(crate) before: Vec<SystemLabelId>,
pub(crate) after: Vec<SystemLabelId>,
pub(crate) ambiguity_sets: Vec<AmbiguitySetLabelId>,
}
impl SystemSet {
@ -72,7 +71,7 @@ impl SystemSet {
#[must_use]
pub fn in_ambiguity_set(mut self, set: impl AmbiguitySetLabel) -> Self {
self.ambiguity_sets.push(Box::new(set));
self.ambiguity_sets.push(set.as_label());
self
}
@ -90,19 +89,19 @@ impl SystemSet {
#[must_use]
pub fn label(mut self, label: impl SystemLabel) -> Self {
self.labels.push(Box::new(label));
self.labels.push(label.as_label());
self
}
#[must_use]
pub fn before<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> Self {
self.before.push(Box::new(label.as_system_label()));
self.before.push(label.as_system_label().as_label());
self
}
#[must_use]
pub fn after<Marker>(mut self, label: impl AsSystemLabel<Marker>) -> Self {
self.after.push(Box::new(label.as_system_label()));
self.after.push(label.as_system_label().as_label());
self
}

View file

@ -4,7 +4,7 @@ use crate::{
component::ComponentId,
prelude::FromWorld,
query::{Access, FilteredAccessSet},
schedule::SystemLabel,
schedule::{SystemLabel, SystemLabelId},
system::{
check_system_change_tick, ReadOnlySystemParamFetch, System, SystemParam, SystemParamFetch,
SystemParamItem, SystemParamState,
@ -12,7 +12,7 @@ use crate::{
world::{World, WorldId},
};
use bevy_ecs_macros::all_tuples;
use std::{borrow::Cow, fmt::Debug, hash::Hash, marker::PhantomData};
use std::{borrow::Cow, fmt::Debug, marker::PhantomData};
/// The metadata of a [`System`].
#[derive(Clone)]
@ -444,14 +444,21 @@ where
self.system_meta.name.as_ref(),
);
}
fn default_labels(&self) -> Vec<Box<dyn SystemLabel>> {
vec![Box::new(self.func.as_system_label())]
fn default_labels(&self) -> Vec<SystemLabelId> {
vec![self.func.as_system_label().as_label()]
}
}
/// A [`SystemLabel`] that was automatically generated for a system on the basis of its `TypeId`.
pub struct SystemTypeIdLabel<T: 'static>(PhantomData<fn() -> T>);
impl<T: 'static> SystemLabel for SystemTypeIdLabel<T> {
#[inline]
fn as_str(&self) -> &'static str {
std::any::type_name::<T>()
}
}
impl<T> Debug for SystemTypeIdLabel<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SystemTypeIdLabel")
@ -459,34 +466,14 @@ impl<T> Debug for SystemTypeIdLabel<T> {
.finish()
}
}
impl<T> Hash for SystemTypeIdLabel<T> {
fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {
// All SystemTypeIds of a given type are the same.
}
}
impl<T> Clone for SystemTypeIdLabel<T> {
fn clone(&self) -> Self {
Self(PhantomData)
*self
}
}
impl<T> Copy for SystemTypeIdLabel<T> {}
impl<T> PartialEq for SystemTypeIdLabel<T> {
#[inline]
fn eq(&self, _other: &Self) -> bool {
// All labels of a given type are equal, as they will all have the same type id
true
}
}
impl<T> Eq for SystemTypeIdLabel<T> {}
impl<T> SystemLabel for SystemTypeIdLabel<T> {
fn dyn_clone(&self) -> Box<dyn SystemLabel> {
Box::new(*self)
}
}
/// A trait implemented for all functions that can be used as [`System`]s.
///
/// This trait can be useful for making your own systems which accept other systems,
@ -612,24 +599,21 @@ all_tuples!(impl_system_function, 0, 16, F);
/// Used to implicitly convert systems to their default labels. For example, it will convert
/// "system functions" to their [`SystemTypeIdLabel`].
pub trait AsSystemLabel<Marker> {
type SystemLabel: SystemLabel;
fn as_system_label(&self) -> Self::SystemLabel;
fn as_system_label(&self) -> SystemLabelId;
}
impl<In, Out, Param: SystemParam, Marker, T: SystemParamFunction<In, Out, Param, Marker>>
AsSystemLabel<(In, Out, Param, Marker)> for T
{
type SystemLabel = SystemTypeIdLabel<Self>;
fn as_system_label(&self) -> Self::SystemLabel {
SystemTypeIdLabel(PhantomData::<fn() -> Self>)
#[inline]
fn as_system_label(&self) -> SystemLabelId {
SystemTypeIdLabel::<T>(PhantomData).as_label()
}
}
impl<T: SystemLabel + Clone> AsSystemLabel<()> for T {
type SystemLabel = T;
fn as_system_label(&self) -> Self::SystemLabel {
self.clone()
impl<T: SystemLabel> AsSystemLabel<()> for T {
#[inline]
fn as_system_label(&self) -> SystemLabelId {
self.as_label()
}
}

View file

@ -2,7 +2,7 @@ use bevy_utils::tracing::warn;
use crate::{
archetype::ArchetypeComponentId, change_detection::MAX_CHANGE_AGE, component::ComponentId,
query::Access, schedule::SystemLabel, world::World,
query::Access, schedule::SystemLabelId, world::World,
};
use std::borrow::Cow;
@ -56,7 +56,7 @@ pub trait System: Send + Sync + 'static {
fn update_archetype_component_access(&mut self, world: &World);
fn check_change_tick(&mut self, change_tick: u32);
/// The default labels for the system
fn default_labels(&self) -> Vec<Box<dyn SystemLabel>> {
fn default_labels(&self) -> Vec<SystemLabelId> {
Vec::new()
}
}

View file

@ -118,12 +118,41 @@ pub fn derive_label(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStr
where_token: Default::default(),
predicates: Default::default(),
});
where_clause.predicates.push(syn::parse2(quote! { Self: Eq + ::std::fmt::Debug + ::std::hash::Hash + Clone + Send + Sync + 'static }).unwrap());
where_clause
.predicates
.push(syn::parse2(quote! { Self: 'static }).unwrap());
let as_str = match input.data {
syn::Data::Struct(d) => match d.fields {
syn::Fields::Unit => {
let lit = ident.to_string();
quote! { #lit }
}
_ => panic!("Labels cannot contain data."),
},
syn::Data::Enum(d) => {
let arms = d.variants.iter().map(|v| match v.fields {
syn::Fields::Unit => {
let mut path = syn::Path::from(ident.clone());
path.segments.push(v.ident.clone().into());
let lit = format!("{ident}::{}", v.ident.clone());
quote! { #path => #lit }
}
_ => panic!("Label variants cannot contain data."),
});
quote! {
match self {
#(#arms),*
}
}
}
syn::Data::Union(_) => panic!("Unions cannot be used as labels."),
};
(quote! {
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
fn dyn_clone(&self) -> std::boxed::Box<dyn #trait_path> {
std::boxed::Box::new(std::clone::Clone::clone(self))
fn as_str(&self) -> &'static str {
#as_str
}
}
})

View file

@ -14,6 +14,7 @@ tracing = { version = "0.1", default-features = false, features = ["std"] }
instant = { version = "0.1", features = ["wasm-bindgen"] }
uuid = { version = "1.1", features = ["v4", "serde"] }
hashbrown = { version = "0.12", features = ["serde"] }
concat-idents = "1"
[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = {version = "0.2.0", features = ["js"]}

View file

@ -47,6 +47,9 @@ where
}
}
#[doc(hidden)]
pub use concat_idents::concat_idents;
/// Macro to define a new label trait
///
/// # Example
@ -57,45 +60,57 @@ where
/// ```
#[macro_export]
macro_rules! define_label {
($label_trait_name:ident) => {
/// Defines a set of strongly-typed labels for a class of objects
pub trait $label_trait_name:
$crate::label::DynHash + ::std::fmt::Debug + Send + Sync + 'static
{
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn $label_trait_name>;
}
($label_name:ident) => {
$crate::label::concat_idents!(id_name = $label_name, Id {
impl PartialEq for dyn $label_trait_name {
fn eq(&self, other: &Self) -> bool {
self.dyn_eq(other.as_dyn_eq())
/// Stores one of a set of strongly-typed labels for a class of objects.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct id_name(::core::any::TypeId, &'static str);
impl ::core::fmt::Debug for id_name {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
write!(f, "{}", self.1)
}
}
}
impl Eq for dyn $label_trait_name {}
impl ::std::hash::Hash for dyn $label_trait_name {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
self.dyn_hash(state);
/// Types that can be converted to a(n) [`id_name`].
///
/// Check the docs for [`define_label`](bevy_ecs::define_label) for more info.
pub trait $label_name: 'static {
/// Converts this type into an opaque, strongly-typed label.
fn as_label(&self) -> id_name {
let id = self.type_id();
let label = self.as_str();
id_name(id, label)
}
/// Returns the [`TypeId`] used to differentiate labels.
fn type_id(&self) -> ::core::any::TypeId {
::core::any::TypeId::of::<Self>()
}
/// Returns the representation of this label as a string literal.
///
/// In cases where you absolutely need a label to be determined at runtime,
/// you can use [`Box::leak`] to get a `'static` reference.
fn as_str(&self) -> &'static str;
}
}
impl Clone for Box<dyn $label_trait_name> {
fn clone(&self) -> Self {
self.dyn_clone()
impl $label_name for id_name {
fn as_label(&self) -> Self {
*self
}
fn type_id(&self) -> ::core::any::TypeId {
self.0
}
fn as_str(&self) -> &'static str {
self.1
}
}
}
impl $label_trait_name for ::std::borrow::Cow<'static, str> {
fn dyn_clone(&self) -> Box<dyn $label_trait_name> {
Box::new(self.clone())
impl $label_name for &'static str {
fn as_str(&self) -> Self {
self
}
}
}
impl $label_trait_name for &'static str {
fn dyn_clone(&self) -> Box<dyn $label_trait_name> {
Box::new(<&str>::clone(self))
}
}
});
};
}