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

[Merged by Bors] - Support piping exclusive systems #7023

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
88 changes: 65 additions & 23 deletions crates/bevy_ecs/src/system/exclusive_function_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
schedule::{SystemLabel, SystemLabelId},
system::{
check_system_change_tick, AsSystemLabel, ExclusiveSystemParam, ExclusiveSystemParamItem,
IntoSystem, System, SystemMeta, SystemTypeIdLabel,
In, InputMarker, IntoSystem, System, SystemMeta, SystemTypeIdLabel,
},
world::{World, WorldId},
};
Expand All @@ -19,7 +19,7 @@ use std::{borrow::Cow, marker::PhantomData};
/// [`ExclusiveSystemParam`]s.
///
/// [`ExclusiveFunctionSystem`] must be `.initialized` before they can be run.
pub struct ExclusiveFunctionSystem<Param, Marker, F>
pub struct ExclusiveFunctionSystem<In, Out, Param, Marker, F>
where
Param: ExclusiveSystemParam,
{
Expand All @@ -28,18 +28,21 @@ where
system_meta: SystemMeta,
world_id: Option<WorldId>,
// NOTE: PhantomData<fn()-> T> gives this safe Send/Sync impls

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that comment outdated now?

marker: PhantomData<fn() -> Marker>,
marker: PhantomData<fn(In) -> (Out, Marker)>,
}

pub struct IsExclusiveFunctionSystem;

impl<Param, Marker, F> IntoSystem<(), (), (IsExclusiveFunctionSystem, Param, Marker)> for F
impl<In, Out, Param, Marker, F> IntoSystem<In, Out, (IsExclusiveFunctionSystem, Param, Marker)>
for F
where
In: 'static,
Out: 'static,
Param: ExclusiveSystemParam + 'static,
Marker: 'static,
F: ExclusiveSystemParamFunction<Param, Marker> + Send + Sync + 'static,
F: ExclusiveSystemParamFunction<In, Out, Param, Marker> + Send + Sync + 'static,
{
type System = ExclusiveFunctionSystem<Param, Marker, F>;
type System = ExclusiveFunctionSystem<In, Out, Param, Marker, F>;
fn into_system(func: Self) -> Self::System {
ExclusiveFunctionSystem {
func,
Expand All @@ -53,14 +56,16 @@ where

const PARAM_MESSAGE: &str = "System's param_state was not found. Did you forget to initialize this system before running it?";

impl<Param, Marker, F> System for ExclusiveFunctionSystem<Param, Marker, F>
impl<In, Out, Param, Marker, F> System for ExclusiveFunctionSystem<In, Out, Param, Marker, F>
where
In: 'static,
Out: 'static,
Param: ExclusiveSystemParam + 'static,
Marker: 'static,
F: ExclusiveSystemParamFunction<Param, Marker> + Send + Sync + 'static,
F: ExclusiveSystemParamFunction<In, Out, Param, Marker> + Send + Sync + 'static,
{
type In = ();
type Out = ();
type In = In;
type Out = Out;

#[inline]
fn name(&self) -> Cow<'static, str> {
Expand Down Expand Up @@ -90,20 +95,22 @@ where
panic!("Cannot run exclusive systems with a shared World reference");
}

fn run(&mut self, _input: Self::In, world: &mut World) -> Self::Out {
fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
let saved_last_tick = world.last_change_tick;
world.last_change_tick = self.system_meta.last_change_tick;

let params = Param::get_param(
self.param_state.as_mut().expect(PARAM_MESSAGE),
&self.system_meta,
);
self.func.run(world, params);
let out = self.func.run(world, input, params);

let change_tick = world.change_tick.get_mut();
self.system_meta.last_change_tick = *change_tick;
*change_tick += 1;
world.last_change_tick = saved_last_tick;

out
}

#[inline]
Expand Down Expand Up @@ -147,8 +154,11 @@ where
}
}

impl<Param: ExclusiveSystemParam, Marker, T: ExclusiveSystemParamFunction<Param, Marker>>
AsSystemLabel<(Param, Marker, IsExclusiveFunctionSystem)> for T
impl<In, Out, Param, Marker, T> AsSystemLabel<(In, Out, Param, Marker, IsExclusiveFunctionSystem)>
for T
where
Param: ExclusiveSystemParam,
T: ExclusiveSystemParamFunction<In, Out, Param, Marker>,
{
#[inline]
fn as_system_label(&self) -> SystemLabelId {
Expand All @@ -160,38 +170,70 @@ impl<Param: ExclusiveSystemParam, Marker, T: ExclusiveSystemParamFunction<Param,
///
/// This trait can be useful for making your own systems which accept other systems,
/// sometimes called higher order systems.
pub trait ExclusiveSystemParamFunction<Param: ExclusiveSystemParam, Marker>:
pub trait ExclusiveSystemParamFunction<In, Out, Param: ExclusiveSystemParam, Marker>:
Send + Sync + 'static
{
fn run(&mut self, world: &mut World, param_value: ExclusiveSystemParamItem<Param>);
fn run(
&mut self,
world: &mut World,
input: In,
param_value: ExclusiveSystemParamItem<Param>,
) -> Out;
}

macro_rules! impl_exclusive_system_function {
($($param: ident),*) => {
#[allow(non_snake_case)]
impl<Func: Send + Sync + 'static, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<($($param,)*), ()> for Func
impl<Out, Func: Send + Sync + 'static, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<(), Out, ($($param,)*), ()> for Func
where
for <'a> &'a mut Func:
FnMut(&mut World, $($param),*) +
FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*)
FnMut(&mut World, $($param),*) -> Out +
FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
Out: 'static,
{
#[inline]
fn run(&mut self, world: &mut World, param_value: ExclusiveSystemParamItem< ($($param,)*)>) {
fn run(&mut self, world: &mut World, _in: (), param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
// Yes, this is strange, but `rustc` fails to compile this impl
// without using this function. It fails to recognise that `func`
// is a function, potentially because of the multiple impls of `FnMut`
#[allow(clippy::too_many_arguments)]
fn call_inner<$($param,)*>(
mut f: impl FnMut(&mut World, $($param,)*),
fn call_inner<Out, $($param,)*>(
mut f: impl FnMut(&mut World, $($param,)*) -> Out,
world: &mut World,
$($param: $param,)*
) {
) -> Out {
f(world, $($param,)*)
}
let ($($param,)*) = param_value;
call_inner(self, world, $($param),*)
}
}
#[allow(non_snake_case)]
impl<Input, Out, Func: Send + Sync + 'static, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<Input, Out, ($($param,)*), InputMarker> for Func
where
for <'a> &'a mut Func:
FnMut(In<Input>, &mut World, $($param),*) -> Out +
FnMut(In<Input>, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
Out: 'static,
{
#[inline]
fn run(&mut self, world: &mut World, input: Input, param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
// Yes, this is strange, but `rustc` fails to compile this impl
// without using this function. It fails to recognise that `func`
// is a function, potentially because of the multiple impls of `FnMut`
#[allow(clippy::too_many_arguments)]
fn call_inner<Input, Out, $($param,)*>(
mut f: impl FnMut(In<Input>, &mut World, $($param,)*) -> Out,
input: Input,
world: &mut World,
$($param: $param,)*
) -> Out {
f(In(input), world, $($param,)*)
}
let ($($param,)*) = param_value;
call_inner(self, input, world, $($param),*)
}
}
};
}
// Note that we rely on the highest impl to be <= the highest order of the tuple impls
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/system/function_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ where
world_id: Option<WorldId>,
archetype_generation: ArchetypeGeneration,
// NOTE: PhantomData<fn()-> T> gives this safe Send/Sync impls
marker: PhantomData<fn() -> (In, Out, Marker)>,
marker: PhantomData<fn(In) -> (Out, Marker)>,
}

pub struct IsFunctionSystem;
Expand Down
7 changes: 7 additions & 0 deletions crates/bevy_ecs/src/system/system_piping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,15 @@ pub mod adapter {
unimplemented!()
}

/// Mocks an exclusive system that takes an input and returns an output.
fn exclusive_in_out<A, B>(_: In<A>, _: &mut World) -> B {
unimplemented!()
}

assert_is_system(returning::<Result<u32, std::io::Error>>.pipe(unwrap));
assert_is_system(returning::<Option<()>>.pipe(ignore));
assert_is_system(returning::<&str>.pipe(new(u64::from_str)).pipe(unwrap));
assert_is_system(exclusive_in_out::<(), Result<(), std::io::Error>>.pipe(error));
assert_is_system(returning::<bool>.pipe(exclusive_in_out::<bool, ()>));
}
}