diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 754fcb8598090..54fa0ba5d5f11 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -1332,19 +1332,57 @@ pub mod lifetimeless { /// A helper for using system parameters in generic contexts /// -/// This type is a system parameter which is statically proven to have -/// `Self::Fetch::Item == Self` (ignoring lifetimes for brevity) +/// This type is a [`SystemParam`] adapter which always has +/// `Self::Fetch::Item == Self` (ignoring lifetimes for brevity), +/// no matter the argument [`SystemParam`] (`P`) (other than +/// that `P` must be `'static`) +/// +/// This makes it useful for having arbitrary [`SystemParam`] type arguments +/// to function systems, or for generic types using the [`derive@SystemParam`] +/// derive: /// /// ``` /// # use bevy_ecs::prelude::*; /// use bevy_ecs::system::{SystemParam, StaticSystemParam}; -/// +/// #[derive(SystemParam)] +/// struct GenericParam<'w,'s, T: SystemParam + 'static> { +/// field: StaticSystemParam<'w, 's, T>, +/// } /// fn do_thing_generically(t: StaticSystemParam) {} /// -/// fn test_always_is(){ +/// fn check_always_is_system(){ /// do_thing_generically::.system(); /// } /// ``` +/// Note that in a real case you'd generally want +/// additional bounds on `P`, for your use of the parameter +/// to have a reason to be generic. +/// +/// For example, using this would allow a type to be generic over +/// whether a resource is accessed mutably or not, with +/// impls being bounded on [`P: Deref`](Deref), and +/// [`P: DerefMut`](DerefMut) depending on whether the +/// method requires mutable access or not. +/// +/// The method which doesn't use this type will not compile: +/// ```compile_fail +/// # use bevy_ecs::prelude::*; +/// # use bevy_ecs::system::{SystemParam, StaticSystemParam}; +/// +/// fn do_thing_generically(t: T) {} +/// +/// #[derive(SystemParam)] +/// struct GenericParam<'w,'s, T: SystemParam> { +/// field: T, +/// #[system_param(ignore)] +/// // Use the lifetimes, as the `SystemParam` derive requires them +/// phantom: core::marker::PhantomData<&'w &'s ()> +/// } +/// # fn check_always_is_system(){ +/// # do_thing_generically::.system(); +/// # } +/// ``` +/// pub struct StaticSystemParam<'w, 's, P: SystemParam>(SystemParamItem<'w, 's, P>); impl<'w, 's, P: SystemParam> Deref for StaticSystemParam<'w, 's, P> { @@ -1362,13 +1400,21 @@ impl<'w, 's, P: SystemParam> DerefMut for StaticSystemParam<'w, 's, P> { } impl<'w, 's, P: SystemParam> StaticSystemParam<'w, 's, P> { - pub fn inner(self) -> SystemParamItem<'w, 's, P> { + /// Get the value of the parameter + pub fn into_inner(self) -> SystemParamItem<'w, 's, P> { self.0 } } +/// The [`SystemParamState`] of [`SystemChangeTick`]. pub struct StaticSystemParamState(S, PhantomData P>); +// Safe: This doesn't add any more reads, and the delegated fetch confirms it +unsafe impl<'w, 's, S: ReadOnlySystemParamFetch, P> ReadOnlySystemParamFetch + for StaticSystemParamState +{ +} + impl<'world, 'state, P: SystemParam + 'static> SystemParam for StaticSystemParam<'world, 'state, P> { @@ -1388,6 +1434,7 @@ where world: &'world World, change_tick: u32, ) -> Self::Item { + // Safe: We properly delegate SystemParamState StaticSystemParam(S::get_param(&mut state.0, system_meta, world, change_tick)) } } diff --git a/crates/bevy_render/src/render_asset.rs b/crates/bevy_render/src/render_asset.rs index 4efb2a9511790..b49e5bbced081 100644 --- a/crates/bevy_render/src/render_asset.rs +++ b/crates/bevy_render/src/render_asset.rs @@ -143,7 +143,7 @@ fn prepare_assets( mut prepare_next_frame: ResMut>, param: StaticSystemParam<::Param>, ) { - let mut param = param.inner(); + let mut param = param.into_inner(); let mut queued_assets = std::mem::take(&mut prepare_next_frame.assets); for (handle, extracted_asset) in queued_assets.drain(..) { match R::prepare_asset(extracted_asset, &mut param) {