diff --git a/benches/iter_entity_add.rs b/benches/iter_entity_add.rs index 0eb0c88b4d..1bc723a03a 100644 --- a/benches/iter_entity_add.rs +++ b/benches/iter_entity_add.rs @@ -20,22 +20,17 @@ struct VelocityZ(f64); #[system] fn system_individual_add_system_non_chunked( - mut px: impl system::WriteSimple, - mut py: impl system::WriteSimple, - mut pz: impl system::WriteSimple, - vx: impl system::ReadSimple, - vy: impl system::ReadSimple, - vz: impl system::ReadSimple, - entities: impl system::EntityIterator, + mut px: system::WriteSimple, + mut py: system::WriteSimple, + mut pz: system::WriteSimple, + vx: system::ReadSimple, + vy: system::ReadSimple, + vz: system::ReadSimple, + entities: system::EntityIterator, ) { - for (_, (px, py, pz, vx, vy, vz)) in entities.entities_with(( - px.access_mut(), - py.access_mut(), - pz.access_mut(), - vx.access(), - vy.access(), - vz.access(), - )) { + for (_, (px, py, pz, vx, vy, vz)) in + entities.entities_with((&mut px, &mut py, &mut pz, &vx, &vy, &vz)) + { px.0 += vx.0; py.0 += vy.0; pz.0 += vz.0; @@ -74,22 +69,17 @@ fn iter_entity_add_individual_non_chunked(group: &mut BenchmarkGroup<'_, measure #[system] fn system_individual_add_system_chunked( - mut px: impl system::WriteSimple, - mut py: impl system::WriteSimple, - mut pz: impl system::WriteSimple, - vx: impl system::ReadSimple, - vy: impl system::ReadSimple, - vz: impl system::ReadSimple, - entities: impl system::EntityIterator, + mut px: system::WriteSimple, + mut py: system::WriteSimple, + mut pz: system::WriteSimple, + vx: system::ReadSimple, + vy: system::ReadSimple, + vz: system::ReadSimple, + entities: system::EntityIterator, ) { - for (_, (px, py, pz, vx, vy, vz)) in entities.chunks_with(( - px.access_chunk_mut(), - py.access_chunk_mut(), - pz.access_chunk_mut(), - vx.access_chunk(), - vy.access_chunk(), - vz.access_chunk(), - )) { + for (_, (px, py, pz, vx, vy, vz)) in + entities.chunks_with((&mut px, &mut py, &mut pz, &vx, &vy, &vz)) + { for (px, (py, (pz, (vx, (vy, vz))))) in iter::zip(px, iter::zip(py, iter::zip(pz, iter::zip(vx, iter::zip(vy, vz))))) { @@ -136,11 +126,11 @@ struct VelocityArray([f64; 3]); #[system] fn system_array_add_system_non_chunked( - mut p: impl system::WriteSimple, - v: impl system::ReadSimple, - entities: impl system::EntityIterator, + mut p: system::WriteSimple, + v: system::ReadSimple, + entities: system::EntityIterator, ) { - for (_, (p, v)) in entities.entities_with((p.access_mut(), v.access())) { + for (_, (p, v)) in entities.entities_with((&mut p, &v)) { for i in 0..3 { p.0[i] += v.0[i]; } @@ -182,11 +172,11 @@ fn iter_entity_add_array_non_chunked(group: &mut BenchmarkGroup<'_, measurement: #[system] fn system_array_add_system_chunked( - mut p: impl system::WriteSimple, - v: impl system::ReadSimple, - entities: impl system::EntityIterator, + mut p: system::WriteSimple, + v: system::ReadSimple, + entities: system::EntityIterator, ) { - for (_, (p, v)) in entities.chunks_with((p.access_chunk_mut(), v.access_chunk())) { + for (_, (p, v)) in entities.chunks_with((&mut p, &v)) { for (p, v) in iter::zip(p, v) { for i in 0..3 { p.0[i] += v.0[i]; diff --git a/codegen/src/system/arg.rs b/codegen/src/system/arg.rs index bdbff1949a..eebac350eb 100644 --- a/codegen/src/system/arg.rs +++ b/codegen/src/system/arg.rs @@ -77,21 +77,21 @@ fn isotope_partial_builder( return Err(Error::new( args_span, "Cannot infer archetype and component for component access. Specify explicitly \ - with `#[dynec(isotope(arch = X, comp = Y))]`, or use \ - `(Read|Write)Isotope(Full|Isotope)`.", + with `#[dynec(isotope(arch = X, comp = Y, [discrim_set = Z]))]`, or use \ + `(Read|Write)Isotope(Full|Isotope)`.", )); } let &arch = args.get(0).expect("args.len() >= 2"); let &comp = args.get(1).expect("args.len() >= 2"); - let discrim_key = args.get(2).map(|&ty| Box::new(ty.clone())).ok_or(args_span); + let discrim_set = args.get(2).map(|&ty| Box::new(ty.clone())).ok_or(args_span); Ok(ArgType::Isotope { mutable: mutable || ident.starts_with("WriteIsotope"), arch: Box::new(arch.clone()), comp: Box::new(comp.clone()), discrim, - discrim_set: discrim_key, + discrim_set, maybe_uninit, }) }) @@ -288,8 +288,8 @@ fn try_attr_to_arg_type(arg: opt::Arg, attr_span: Span, param_span: Span) -> Res let discrim = opts.find_one( |opt| option_match!(opt, opt::IsotopeArg::Discrim(_, discrim) => discrim), )?; - let discrim_key = opts - .find_one(|opt| option_match!(opt, opt::IsotopeArg::DiscrimKey(_, ty) => ty))? + let discrim_set = opts + .find_one(|opt| option_match!(opt, opt::IsotopeArg::DiscrimSet(_, ty) => ty))? .ok_or(param_span); let maybe_uninit = opts.merge_all(|opt| option_match!(opt, opt::IsotopeArg::MaybeUninit(_, tys) => tys.iter().cloned())); @@ -300,7 +300,7 @@ fn try_attr_to_arg_type(arg: opt::Arg, attr_span: Span, param_span: Span) -> Res arch: arch.clone(), comp: comp.clone(), discrim: discrim.map(|(_, discrim)| discrim.clone()), - discrim_set: discrim_key.map(|(_, ty)| ty.clone()), + discrim_set: discrim_set.map(|(_, ty)| ty.clone()), maybe_uninit, }) } diff --git a/codegen/src/system/opt.rs b/codegen/src/system/opt.rs index b4bb2f5c99..1725c4c400 100644 --- a/codegen/src/system/opt.rs +++ b/codegen/src/system/opt.rs @@ -154,7 +154,7 @@ pub(super) enum IsotopeArg { Arch(syn::Token![=], Box), Comp(syn::Token![=], Box), Discrim(syn::Token![=], Box), - DiscrimKey(syn::Token![=], Box), + DiscrimSet(syn::Token![=], Box), MaybeUninit(syn::token::Paren, Punctuated), } @@ -180,10 +180,10 @@ impl Parse for Named { let discrim = input.parse::()?; IsotopeArg::Discrim(eq, Box::new(discrim)) } - "discrim_key" => { + "discrim_set" => { let eq = input.parse::()?; let ty = input.parse::()?; - IsotopeArg::DiscrimKey(eq, Box::new(ty)) + IsotopeArg::DiscrimSet(eq, Box::new(ty)) } "maybe_uninit" => parse_maybe_uninit(input, IsotopeArg::MaybeUninit)?, _ => return Err(Error::new_spanned(&name, "Unknown option for #[dynec(isotope)]")), diff --git a/src/macros.rs b/src/macros.rs index 4a1c4cc82b..6cabb511ed 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -341,22 +341,24 @@ mod global_tests {} /// ``` /// /// ## Simple components -/// Parameters in the form `impl ReadSimple` or `impl WriteSimple`, +/// Parameters of type `ReadSimple` or `WriteSimple`, /// request access to a [simple component](crate::comp::Simple) of type `C` -/// from entities of the [archetype](crate::Archetype) `A`, -/// exposed through a type that implements [`system::ReadSimple`](crate::system::ReadSimple) -/// or [`system::WriteSimple`](crate::system::WriteSimple). +/// from entities of the [archetype](crate::Archetype) `A`. /// The latter provides mutable and exclusive access to the component storages. /// -/// ### Using other bounds -/// Other trait bounds for the parameter are also allowed, -/// but the macro would not be able to infer type parameters and mutability. +/// ### Using other aliases +/// Using type aliases/renamed imports for the types is also allowed, +/// but the macro would be unable to infer type parameters and mutability. /// In such cases, they must be indicated explicitly in the attribute. /// See the syntax reference below for details. /// -/// ### Uninitialized entity references. -/// Entity creation ordering is automatically enforced if `C` contains entity references, -/// Use the `maybe_uninit` attribute to remove this ordering. +/// ### Uninitialized entity references +/// If `C` contains [references](crate::entity::Referrer) to entities of some archetype `T`, +/// the scheduler automatically enforces that the system runs before +/// any systems that create entities of archetype `T`, +/// because components for entities created through [`EntityCreator`](crate::system::EntityCreator) +/// are uninitialized until the current cycle completes. +/// Use the `maybe_uninit` attribute to remove this ordering limitation. /// /// See [`EntityCreationPartition`](crate::system::partition::EntityCreationPartition#component-accessors) /// for more information. @@ -366,10 +368,10 @@ mod global_tests {} /// # /* /// #[dynec(simple( /// // Optional, specifies the archetype and component explicitly. -/// // Only required when the parameter type is not `impl ReadSimple`/`impl WriteSimple`. +/// // Only required when the parameter type is not `ReadSimple`/`WriteSimple`. /// arch = $ty, comp = $ty, /// // Optional, indicates that the component access is exclusive explicitly. -/// // Only required when the parameter type is not `impl WriteSimple`. +/// // Only required when the parameter type is not `WriteSimple`. /// mut, /// // Optional, acknowledges that the entities of the specified archetypes /// // contained in the simple components may be uninitialized. @@ -379,17 +381,16 @@ mod global_tests {} /// ``` /// /// ## Isotope components -/// Parameters in the form `impl ReadIsotope` or `impl WriteIsotope`, +/// Parameters of type [`(Read|Write)Isotope(Full|Partial)`](mod@crate::system#types) /// request access to an [isotope component](crate::comp::Isotope) of type `C` -/// from entities of the [archetype](crate::Archetype) `A`, -/// exposed through a type that implements [`system::ReadIsotope`](crate::system::ReadIsotope) -/// or [`system::WriteIsotope`](crate::system::WriteIsotope). -/// The latter provides mutable and exclusive access to the component storages. +/// from entities of the [archetype](crate::Archetype) `A`. +/// The `Write` variants provide mutable and exclusive access to the component storages. /// /// ### Partial isotope access -/// By default, all discriminants of the isotope component are requested, -/// such that writes are exclusive with all systems that read any part of the discriminants. -/// The accessor can be made partial instead: +/// If [`ReadIsotopePartial`](crate::system::ReadIsotopePartial) or +/// [`WriteIsotopePartial`](crate::system::WriteIsotopePartial) is used, +/// the system only requests access to specific discriminants of the isotope component. +/// The actual discriminants are specified with an attribute: /// /// ``` /// # /* @@ -397,26 +398,31 @@ mod global_tests {} /// # */ /// ``` /// -/// The expression `discrim_set` implements -/// [discrim::Set](crate::comp::discrim::Set), -/// which is the set of discriminants that this system uses. -/// The expression can reference local and param states directly. -/// However, since it is only evaluated once before the first run of the system, -/// subsequent writes to the states have no effect on the resolved discriminant set. +/// The expression `discrim_set` contains the set of discriminants requested by this system +/// contained in an implementation of +/// [discrim::Set](crate::comp::discrim::Set)<C::[Discrim](crate::comp::Discrim)>, +/// which is typically an array or a [`Vec`]. +/// The expression may reference param states directly. +/// The expression is only evaluated once before the first run of the system, +/// so it will not react to subsequent changes to the param states. /// /// `K` is the type of the [key](crate::comp::discrim::Set::Key) to index the discriminant set. /// /// See the documentation of [`discrim::Set`](crate::comp::discrim::Set) for more information. /// -/// ### Using other bounds -/// Other trait bounds for the parameter are also allowed, -/// but the macro would not be able to infer type parameters and mutability. +/// ### Using other aliases +/// Using type aliases/renamed imports for the types is also allowed, +/// but the macro would be unable to infer type parameters and mutability. /// In such cases, they must be indicated explicitly in the attribute. /// See the syntax reference below for details. /// -/// ### Uninitialized entity references. -/// Entity creation ordering is automatically enforced if `C` contains entity references, -/// Use the `maybe_uninit` attribute to remove this ordering. +/// ### Uninitialized entity references +/// If `C` contains [references](crate::entity::Referrer) to entities of some archetype `T`, +/// the scheduler automatically enforces that the system runs before +/// any systems that create entities of archetype `T`, +/// because components for entities created through [`EntityCreator`](crate::system::EntityCreator) +/// are uninitialized until the current cycle completes. +/// Use the `maybe_uninit` attribute to remove this ordering limitation. /// /// See [`EntityCreationPartition`](crate::system::partition::EntityCreationPartition#component-accessors) /// for more information. @@ -425,13 +431,16 @@ mod global_tests {} /// ``` /// # /* /// #[dynec(isotope( -/// // Optional, indicates that this accessor only uses the given subset of discriminants. +/// // Required if and only if the type is ReadIsotopePartial or WriteIsotopePartial. /// discrim = $expr, -/// // Optional, must be the same as the `Key` associated type of the `discrim` expression. -/// // Only required when the parameter type is not `impl ReadIsotope`/`impl WriteIsotope`. -/// discrim_key = $ty, +/// // Optional, must be the same as the type of the `discrim` expression. +/// // Only required when the parameter type is not `ReadIsotopePartial`/`WriteIsotopePartial`. +/// // Note that `ReadIsotopePartial`/`WriteIsotopePartial` have an optional third type parameter +/// // that expects the same type as `discrim_set`, +/// // which is `Vec` by default. +/// discrim_set = $ty, /// // Optional, specifies the archetype and component explicitly. -/// // Only required when the parameter type is not `impl ReadIsotope`/`impl WriteIsotope`. +/// // Only required when the parameter type is not `(Read|Write)Isotope(Full|Partial)`. /// arch = $ty, comp = $ty, /// // Optional, indicates that the component access is exclusive explicitly. /// // Only required when the parameter type is not `impl WriteSimple`. @@ -444,7 +453,7 @@ mod global_tests {} /// ``` /// /// ## Entity creation -/// Parameters that require an implementation of [`EntityCreator`](crate::system::EntityCreator) +/// Parameters that require an [`EntityCreator`](crate::system::EntityCreator) /// can be used to create entities. /// The archetype of created entities is specified in the type bounds. /// Note that entity creation is asynchronous to ensure synchronization, @@ -478,7 +487,7 @@ mod global_tests {} /// ``` /// /// ## Entity deletion -/// Parameters that require an implementation of [`EntityDeleter`](crate::system::EntityDeleter) +/// Parameters that require an [`EntityDeleter`](crate::system::EntityDeleter) /// can be used to delete entities. /// The archetype of deleted entities is specified in the type bounds. /// Note that `EntityDeleter` can only be used to mark entities as "deleting"; @@ -488,9 +497,11 @@ mod global_tests {} /// It is advisable to execute finalizer-removing systems /// after systems that mark entities for deletion finish executing. /// This allows deletion to happen in the same cycle, -/// thus slightly improving entity deletion performance +/// thus slightly reducing entity deletion latency /// (but this is not supposed to be critical anyway). -/// Nevertheless, unlike entity creation, entity deletion does not have an automatic partition. +/// Nevertheless, unlike entity creation, +/// the scheduler does not automatically enforce ordering between +/// finalizer-manipulating systems and entity-deleting systems. /// /// ### Syntax reference /// ``` @@ -503,6 +514,22 @@ mod global_tests {} /// # */ /// ``` /// +/// ## Entity iterator +/// Parameters that require an [`EntityIterator`](crate::system::EntityIterator) +/// can be used to iterate over entities and zip multiple component iterators. +/// See the documentation for `EntityIterator` for details. +/// +/// ### Syntax reference +/// ``` +/// # /* +/// /// This attribute is not required unless `EntityIterator` is aliased. +/// #[dynec(entity_iterator( +/// // Optional, specifies the archetype if `EntityIterator` is aliased. +/// arch = $ty, +/// ))] +/// # */ +/// ``` +/// /// # Example /// ``` /// use dynec::system; @@ -802,7 +829,7 @@ macro_rules! assert_partition { } /// Declares a composite struct that implements -/// [`IntoZip`](crate::system::access:IntoZip), [`Zip`](crate::system::access::Zip) +/// [`IntoZip`](crate::system::access::IntoZip), [`Zip`](crate::system::access::Zip) /// and [`ZipChunked`](crate::system::access::ZipChunked) /// by delegation to all fields and reconstructing the same struct with different types. /// diff --git a/src/storage.rs b/src/storage.rs index 263a5f825e..cf0d5056b3 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -57,7 +57,7 @@ pub trait Storage: Access + Default + Send + Sync + 'static { /// Non-chunked storages should implement this function by returning a chunk for each entity. fn iter_chunks_mut(&mut self) -> Self::IterChunksMut<'_>; - /// Return value of [`split_at`](Self::split_at). + /// Return value of [`as_partition`](Self::as_partition). type Partition<'u>: Partition<'u, RawEntity = Self::RawEntity, Comp = Self::Comp> where Self: 'u; @@ -77,7 +77,7 @@ pub trait Partition<'t>: Access + Send + Sync + Sized + 't { Self: 'u; /// Re-borrows the partition with reduced lifetime. /// - /// This is useful for calling [`iter_mut`](Self::iter_mut) + /// This is useful for calling [`into_iter_mut`](Self::into_iter_mut) /// and [`split_at`](Self::split_at), /// which take `self` as receiver to preserve the lifetime. fn by_ref(&mut self) -> Self::ByRef<'_>; @@ -101,10 +101,10 @@ pub trait Partition<'t>: Access + Send + Sync + Sized + 't { /// Return value of [`into_iter_mut`](Self::into_iter_mut). type IntoIterMut: Iterator; - /// Same as [`iter_mut`](Access:iter_mut), but moves the partition object into the iterator. + /// Same as [`iter_mut`](Access::iter_mut), but moves the partition object into the iterator. fn into_iter_mut(self) -> Self::IntoIterMut; - /// Same as [`try_get_mut`](Access::try_get_mut), but returns a reference with lifetime `'t`. + /// Same as [`get_mut`](Access::get_mut), but returns a reference with lifetime `'t`. fn into_mut(self, entity: Self::RawEntity) -> Option<&'t mut Self::Comp>; } diff --git a/src/system/access/isotope.rs b/src/system/access/isotope.rs index 16a697d279..6e78c9f960 100644 --- a/src/system/access/isotope.rs +++ b/src/system/access/isotope.rs @@ -180,7 +180,7 @@ where storage.iter().map(|(entity, comp)| (entity::TempRef::new(entity), comp)) } - /// Splits the accessor into multiple [`Read`] implementors + /// Splits the accessor into multiple mmutable [`AccessSingle`] accessors /// so that they can be used independently. pub fn split( &mut self, @@ -306,7 +306,7 @@ where storage.iter_mut().map(|(entity, comp)| (entity::TempRef::new(entity), comp)) } - /// Splits the accessor into multiple [`Write`] implementors + /// Splits the accessor into multiple mutable [`AccessSingle`] accessors /// so that they can be used in entity iteration independently. pub fn split_isotopes( &mut self, diff --git a/src/system/access/iter.rs b/src/system/access/iter.rs index 3a2fcbf6cf..a0d04da707 100644 --- a/src/system/access/iter.rs +++ b/src/system/access/iter.rs @@ -30,7 +30,7 @@ pub trait ZipChunked: Zip { fn get_chunk(self, chunk: entity::TempRefChunk) -> Self::Chunk; } -/// Values that can be used as a [`Zip`] in [`EntityIterator`], +/// Values that can be used as a [`Zip`] in [`EntityIterator`](crate::system::EntityIterator), /// similar to [`IntoIterator`] for iterators. /// /// This trait is intended to map storages to components of a single entity, @@ -39,7 +39,7 @@ pub trait ZipChunked: Zip { /// - Shared/mutable references to [split](super::AccessIsotope::split) isotope accessors /// - Any of the above wrapped with [`Try`] for [optional](comp::Presence::Optional) components. /// - Tuples of `Zip` implementors, including other tuples. -/// - Structs of `Zip` fields that use the [`Zip`](crate::Zip) derive macro. +/// - Structs of `Zip` fields that use the [`Zip`](crate::zip) derive macro. pub trait IntoZip { /// The [`Zip`] type that this is converted into. type IntoZip: Zip; diff --git a/src/system/access/single.rs b/src/system/access/single.rs index 473e3b9f2c..b1b85c4120 100644 --- a/src/system/access/single.rs +++ b/src/system/access/single.rs @@ -139,7 +139,7 @@ where /// /// Note that this function returns `Option<&mut C>`, not `&mut Option`. /// This means setting the Option itself to `Some`/`None` will not modify any stored value. - /// Use [`Write::set`] to add/remove a component. + /// Use [`set`](AccessSingle::set) to add/remove a component. pub fn try_get_mut(&mut self, entity: impl entity::Ref) -> Option<&mut C> { self.storage.get_mut(entity.id()) } @@ -191,10 +191,10 @@ where self.storage.set(entity.id(), value) } - /// Converts the accessor to a [`MutPartition`] that covers all entities. + /// Converts the accessor to a mutably borrowed partition that covers all entities. /// /// The actual splitting partitions can be obtained - /// by calling [`split_at`](Access::split_at) on the returned value. + /// by calling [`split_at`](AccessSingle::split_at) on the returned value. pub fn as_partition( &mut self, ) -> AccessSingle::Partition<'_>>> { diff --git a/src/world/rw/isotope/read/partial.rs b/src/world/rw/isotope/read/partial.rs index 794e057d68..72f07d0051 100644 --- a/src/world/rw/isotope/read/partial.rs +++ b/src/world/rw/isotope/read/partial.rs @@ -16,7 +16,7 @@ use crate::{comp, system, world, Archetype}; /// /// To share the same API as [`ReadIsotopeFull`](system::ReadIsotopeFull), /// immutable getters still require `&mut self`, -/// but there are `*_ref` variants for [`ReadIsotopeFull`] that just require `&self`. +/// but there are `*_ref` variants for these functions that just require `&self`. pub type ReadIsotopePartial>::Discrim>> = system::AccessIsotope>; diff --git a/src/world/rw/isotope/write/partial.rs b/src/world/rw/isotope/write/partial.rs index d8a985fd6e..fd31505720 100644 --- a/src/world/rw/isotope/write/partial.rs +++ b/src/world/rw/isotope/write/partial.rs @@ -16,7 +16,7 @@ use crate::{comp, system, world, Archetype}; /// /// To share the same API as [`ReadIsotopeFull`](system::ReadIsotopeFull), /// immutable getters still require `&mut self`, -/// but there are `*_ref` variants for [`ReadIsotopeFull`] that just require `&self`. +/// but there are `*_ref` variants for these functions that just require `&self`. pub type WriteIsotopePartial>::Discrim>> = system::AccessIsotope>;