Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/system/exclusive_function_system.rs
6604 views
1
use crate::{
2
component::{CheckChangeTicks, Tick},
3
error::Result,
4
query::FilteredAccessSet,
5
schedule::{InternedSystemSet, SystemSet},
6
system::{
7
check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, IntoResult,
8
IntoSystem, System, SystemIn, SystemInput, SystemMeta,
9
},
10
world::{unsafe_world_cell::UnsafeWorldCell, World},
11
};
12
13
use alloc::{borrow::Cow, vec, vec::Vec};
14
use bevy_utils::prelude::DebugName;
15
use core::marker::PhantomData;
16
use variadics_please::all_tuples;
17
18
use super::{RunSystemError, SystemParamValidationError, SystemStateFlags};
19
20
/// A function system that runs with exclusive [`World`] access.
21
///
22
/// You get this by calling [`IntoSystem::into_system`] on a function that only accepts
23
/// [`ExclusiveSystemParam`]s.
24
///
25
/// [`ExclusiveFunctionSystem`] must be `.initialized` before they can be run.
26
pub struct ExclusiveFunctionSystem<Marker, Out, F>
27
where
28
F: ExclusiveSystemParamFunction<Marker>,
29
{
30
func: F,
31
#[cfg(feature = "hotpatching")]
32
current_ptr: subsecond::HotFnPtr,
33
param_state: Option<<F::Param as ExclusiveSystemParam>::State>,
34
system_meta: SystemMeta,
35
// NOTE: PhantomData<fn()-> T> gives this safe Send/Sync impls
36
marker: PhantomData<fn() -> (Marker, Out)>,
37
}
38
39
impl<Marker, Out, F> ExclusiveFunctionSystem<Marker, Out, F>
40
where
41
F: ExclusiveSystemParamFunction<Marker>,
42
{
43
/// Return this system with a new name.
44
///
45
/// Useful to give closure systems more readable and unique names for debugging and tracing.
46
pub fn with_name(mut self, new_name: impl Into<Cow<'static, str>>) -> Self {
47
self.system_meta.set_name(new_name);
48
self
49
}
50
}
51
52
/// A marker type used to distinguish exclusive function systems from regular function systems.
53
#[doc(hidden)]
54
pub struct IsExclusiveFunctionSystem;
55
56
impl<Out, Marker, F> IntoSystem<F::In, Out, (IsExclusiveFunctionSystem, Marker, Out)> for F
57
where
58
Out: 'static,
59
Marker: 'static,
60
F::Out: IntoResult<Out>,
61
F: ExclusiveSystemParamFunction<Marker>,
62
{
63
type System = ExclusiveFunctionSystem<Marker, Out, F>;
64
fn into_system(func: Self) -> Self::System {
65
ExclusiveFunctionSystem {
66
func,
67
#[cfg(feature = "hotpatching")]
68
current_ptr: subsecond::HotFn::current(
69
<F as ExclusiveSystemParamFunction<Marker>>::run,
70
)
71
.ptr_address(),
72
param_state: None,
73
system_meta: SystemMeta::new::<F>(),
74
marker: PhantomData,
75
}
76
}
77
}
78
79
const PARAM_MESSAGE: &str = "System's param_state was not found. Did you forget to initialize this system before running it?";
80
81
impl<Marker, Out, F> System for ExclusiveFunctionSystem<Marker, Out, F>
82
where
83
Marker: 'static,
84
Out: 'static,
85
F::Out: IntoResult<Out>,
86
F: ExclusiveSystemParamFunction<Marker>,
87
{
88
type In = F::In;
89
type Out = Out;
90
91
#[inline]
92
fn name(&self) -> DebugName {
93
self.system_meta.name.clone()
94
}
95
96
#[inline]
97
fn flags(&self) -> SystemStateFlags {
98
// non-send , exclusive , no deferred
99
// the executor runs exclusive systems on the main thread, so this
100
// field reflects that constraint
101
// exclusive systems have no deferred system params
102
SystemStateFlags::NON_SEND | SystemStateFlags::EXCLUSIVE
103
}
104
105
#[inline]
106
unsafe fn run_unsafe(
107
&mut self,
108
input: SystemIn<'_, Self>,
109
world: UnsafeWorldCell,
110
) -> Result<Self::Out, RunSystemError> {
111
// SAFETY: The safety is upheld by the caller.
112
let world = unsafe { world.world_mut() };
113
world.last_change_tick_scope(self.system_meta.last_run, |world| {
114
#[cfg(feature = "trace")]
115
let _span_guard = self.system_meta.system_span.enter();
116
117
let params = F::Param::get_param(
118
self.param_state.as_mut().expect(PARAM_MESSAGE),
119
&self.system_meta,
120
);
121
122
#[cfg(feature = "hotpatching")]
123
let out = {
124
let mut hot_fn =
125
subsecond::HotFn::current(<F as ExclusiveSystemParamFunction<Marker>>::run);
126
// SAFETY:
127
// - pointer used to call is from the current jump table
128
unsafe {
129
hot_fn
130
.try_call_with_ptr(self.current_ptr, (&mut self.func, world, input, params))
131
.expect("Error calling hotpatched system. Run a full rebuild")
132
}
133
};
134
#[cfg(not(feature = "hotpatching"))]
135
let out = self.func.run(world, input, params);
136
137
world.flush();
138
self.system_meta.last_run = world.increment_change_tick();
139
140
IntoResult::into_result(out)
141
})
142
}
143
144
#[cfg(feature = "hotpatching")]
145
#[inline]
146
fn refresh_hotpatch(&mut self) {
147
let new = subsecond::HotFn::current(<F as ExclusiveSystemParamFunction<Marker>>::run)
148
.ptr_address();
149
if new != self.current_ptr {
150
log::debug!("system {} hotpatched", self.name());
151
}
152
self.current_ptr = new;
153
}
154
155
#[inline]
156
fn apply_deferred(&mut self, _world: &mut World) {
157
// "pure" exclusive systems do not have any buffers to apply.
158
// Systems made by piping a normal system with an exclusive system
159
// might have buffers to apply, but this is handled by `PipeSystem`.
160
}
161
162
#[inline]
163
fn queue_deferred(&mut self, _world: crate::world::DeferredWorld) {
164
// "pure" exclusive systems do not have any buffers to apply.
165
// Systems made by piping a normal system with an exclusive system
166
// might have buffers to apply, but this is handled by `PipeSystem`.
167
}
168
169
#[inline]
170
unsafe fn validate_param_unsafe(
171
&mut self,
172
_world: UnsafeWorldCell,
173
) -> Result<(), SystemParamValidationError> {
174
// All exclusive system params are always available.
175
Ok(())
176
}
177
178
#[inline]
179
fn initialize(&mut self, world: &mut World) -> FilteredAccessSet {
180
self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX);
181
self.param_state = Some(F::Param::init(world, &mut self.system_meta));
182
FilteredAccessSet::new()
183
}
184
185
#[inline]
186
fn check_change_tick(&mut self, check: CheckChangeTicks) {
187
check_system_change_tick(
188
&mut self.system_meta.last_run,
189
check,
190
self.system_meta.name.clone(),
191
);
192
}
193
194
fn default_system_sets(&self) -> Vec<InternedSystemSet> {
195
let set = crate::schedule::SystemTypeSet::<Self>::new();
196
vec![set.intern()]
197
}
198
199
fn get_last_run(&self) -> Tick {
200
self.system_meta.last_run
201
}
202
203
fn set_last_run(&mut self, last_run: Tick) {
204
self.system_meta.last_run = last_run;
205
}
206
}
207
208
/// A trait implemented for all exclusive system functions that can be used as [`System`]s.
209
///
210
/// This trait can be useful for making your own systems which accept other systems,
211
/// sometimes called higher order systems.
212
#[diagnostic::on_unimplemented(
213
message = "`{Self}` is not an exclusive system",
214
label = "invalid system"
215
)]
216
pub trait ExclusiveSystemParamFunction<Marker>: Send + Sync + 'static {
217
/// The input type to this system. See [`System::In`].
218
type In: SystemInput;
219
220
/// The return type of this system. See [`System::Out`].
221
type Out;
222
223
/// The [`ExclusiveSystemParam`]'s defined by this system's `fn` parameters.
224
type Param: ExclusiveSystemParam;
225
226
/// Executes this system once. See [`System::run`].
227
fn run(
228
&mut self,
229
world: &mut World,
230
input: <Self::In as SystemInput>::Inner<'_>,
231
param_value: ExclusiveSystemParamItem<Self::Param>,
232
) -> Self::Out;
233
}
234
235
/// A marker type used to distinguish exclusive function systems with and without input.
236
#[doc(hidden)]
237
pub struct HasExclusiveSystemInput;
238
239
macro_rules! impl_exclusive_system_function {
240
($($param: ident),*) => {
241
#[expect(
242
clippy::allow_attributes,
243
reason = "This is within a macro, and as such, the below lints may not always apply."
244
)]
245
#[allow(
246
non_snake_case,
247
reason = "Certain variable names are provided by the caller, not by us."
248
)]
249
impl<Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<fn($($param,)*) -> Out> for Func
250
where
251
Func: Send + Sync + 'static,
252
for <'a> &'a mut Func:
253
FnMut(&mut World, $($param),*) -> Out +
254
FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
255
Out: 'static,
256
{
257
type In = ();
258
type Out = Out;
259
type Param = ($($param,)*);
260
#[inline]
261
fn run(&mut self, world: &mut World, _in: (), param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
262
// Yes, this is strange, but `rustc` fails to compile this impl
263
// without using this function. It fails to recognize that `func`
264
// is a function, potentially because of the multiple impls of `FnMut`
265
fn call_inner<Out, $($param,)*>(
266
mut f: impl FnMut(&mut World, $($param,)*) -> Out,
267
world: &mut World,
268
$($param: $param,)*
269
) -> Out {
270
f(world, $($param,)*)
271
}
272
let ($($param,)*) = param_value;
273
call_inner(self, world, $($param),*)
274
}
275
}
276
277
#[expect(
278
clippy::allow_attributes,
279
reason = "This is within a macro, and as such, the below lints may not always apply."
280
)]
281
#[allow(
282
non_snake_case,
283
reason = "Certain variable names are provided by the caller, not by us."
284
)]
285
impl<In, Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<(HasExclusiveSystemInput, fn(In, $($param,)*) -> Out)> for Func
286
where
287
Func: Send + Sync + 'static,
288
for <'a> &'a mut Func:
289
FnMut(In, &mut World, $($param),*) -> Out +
290
FnMut(In::Param<'_>, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
291
In: SystemInput + 'static,
292
Out: 'static,
293
{
294
type In = In;
295
type Out = Out;
296
type Param = ($($param,)*);
297
#[inline]
298
fn run(&mut self, world: &mut World, input: In::Inner<'_>, param_value: ExclusiveSystemParamItem< ($($param,)*)>) -> Out {
299
// Yes, this is strange, but `rustc` fails to compile this impl
300
// without using this function. It fails to recognize that `func`
301
// is a function, potentially because of the multiple impls of `FnMut`
302
fn call_inner<In: SystemInput, Out, $($param,)*>(
303
_: PhantomData<In>,
304
mut f: impl FnMut(In::Param<'_>, &mut World, $($param,)*) -> Out,
305
input: In::Inner<'_>,
306
world: &mut World,
307
$($param: $param,)*
308
) -> Out {
309
f(In::wrap(input), world, $($param,)*)
310
}
311
let ($($param,)*) = param_value;
312
call_inner(PhantomData::<In>, self, input, world, $($param),*)
313
}
314
}
315
};
316
}
317
// Note that we rely on the highest impl to be <= the highest order of the tuple impls
318
// of `SystemParam` created.
319
all_tuples!(impl_exclusive_system_function, 0, 16, F);
320
321
#[cfg(test)]
322
mod tests {
323
use crate::system::input::SystemInput;
324
325
use super::*;
326
327
#[test]
328
fn into_system_type_id_consistency() {
329
fn test<T, In: SystemInput, Out, Marker>(function: T)
330
where
331
T: IntoSystem<In, Out, Marker> + Copy,
332
{
333
fn reference_system(_world: &mut World) {}
334
335
use core::any::TypeId;
336
337
let system = IntoSystem::into_system(function);
338
339
assert_eq!(
340
system.type_id(),
341
function.system_type_id(),
342
"System::type_id should be consistent with IntoSystem::system_type_id"
343
);
344
345
assert_eq!(
346
system.type_id(),
347
TypeId::of::<T::System>(),
348
"System::type_id should be consistent with TypeId::of::<T::System>()"
349
);
350
351
assert_ne!(
352
system.type_id(),
353
IntoSystem::into_system(reference_system).type_id(),
354
"Different systems should have different TypeIds"
355
);
356
}
357
358
fn exclusive_function_system(_world: &mut World) {}
359
360
test(exclusive_function_system);
361
}
362
}
363
364