ecs: ergonomic query.iter(), remove locks, add QuerySets (#741)

This commit is contained in:
Carter Anderson 2020-10-29 23:39:55 -07:00 committed by GitHub
parent f41f2069a0
commit 1d4a95db62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 1317 additions and 1493 deletions

View File

@ -70,10 +70,10 @@ pub(crate) fn entity_labels_system(
mut entity_labels: ResMut<EntityLabels>,
// TODO: use change tracking when add/remove events are added
// mut query: Query<(Entity, Changed<Labels>)>,
mut query: Query<(Entity, &Labels)>,
query: Query<(Entity, &Labels)>,
) {
let entity_labels = entity_labels.deref_mut();
for (entity, labels) in &mut query.iter() {
for (entity, labels) in query.iter() {
let current_labels = entity_labels
.entity_labels
.entry(entity)

View File

@ -57,7 +57,7 @@ impl Timer {
}
pub(crate) fn timer_system(time: Res<Time>, mut query: Query<&mut Timer>) {
for mut timer in &mut query.iter() {
for mut timer in query.iter_mut() {
timer.tick(time.delta_seconds);
}
}

View File

@ -20,7 +20,7 @@ use proc_macro::TokenStream;
use proc_macro2::Span;
use proc_macro_crate::crate_name;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Path};
use syn::{parse_macro_input, DeriveInput, Ident, Index, Lifetime, Path};
/// Implement `Bundle` for a monomorphic struct
///
@ -141,3 +141,76 @@ fn struct_fields(fields: &syn::Fields) -> (Vec<&syn::Type>, Vec<syn::Ident>) {
syn::Fields::Unit => (Vec::new(), Vec::new()),
}
}
fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec<Ident> {
(0..count)
.map(|i| Ident::new(&fmt_string(i), Span::call_site()))
.collect::<Vec<Ident>>()
}
fn get_lifetimes(fmt_string: fn(usize) -> String, count: usize) -> Vec<Lifetime> {
(0..count)
.map(|i| Lifetime::new(&fmt_string(i), Span::call_site()))
.collect::<Vec<Lifetime>>()
}
#[proc_macro]
pub fn impl_query_set(_input: TokenStream) -> TokenStream {
let mut tokens = TokenStream::new();
let max_queries = 4;
let queries = get_idents(|i| format!("Q{}", i), max_queries);
let lifetimes = get_lifetimes(|i| format!("'q{}", i), max_queries);
let mut query_fns = Vec::new();
let mut query_fn_muts = Vec::new();
for i in 0..max_queries {
let query = &queries[i];
let lifetime = &lifetimes[i];
let fn_name = Ident::new(&format!("q{}", i), Span::call_site());
let fn_name_mut = Ident::new(&format!("q{}_mut", i), Span::call_site());
let index = Index::from(i);
query_fns.push(quote! {
pub fn #fn_name(&self) -> &Query<#lifetime, #query> {
&self.value.#index
}
});
query_fn_muts.push(quote! {
pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #query> {
&mut self.value.#index
}
});
}
for query_count in 1..=max_queries {
let query = &queries[0..query_count];
let lifetime = &lifetimes[0..query_count];
let query_fn = &query_fns[0..query_count];
let query_fn_mut = &query_fn_muts[0..query_count];
tokens.extend(TokenStream::from(quote! {
impl<#(#lifetime,)* #(#query: HecsQuery,)*> QueryTuple for (#(Query<#lifetime, #query>,)*) {
unsafe fn new(world: &World, component_access: &TypeAccess<ArchetypeComponent>) -> Self {
(
#(
Query::<#query>::new(
std::mem::transmute(world),
std::mem::transmute(component_access),
),
)*
)
}
fn get_accesses() -> Vec<QueryAccess> {
vec![
#(<#query::Fetch as Fetch>::access(),)*
]
}
}
impl<#(#lifetime,)* #(#query: HecsQuery,)*> QuerySet<(#(Query<#lifetime, #query>,)*)> {
#(#query_fn)*
#(#query_fn_mut)*
}
}));
}
tokens
}

View File

@ -0,0 +1,364 @@
use core::{any::TypeId, hash::Hash};
use std::{boxed::Box, vec::Vec};
use crate::{Archetype, World};
use bevy_utils::HashSet;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub enum Access {
None,
Read,
Write,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct ArchetypeComponent {
pub archetype_index: u32,
pub component: TypeId,
}
impl ArchetypeComponent {
#[inline]
pub fn new<T: 'static>(archetype_index: u32) -> Self {
ArchetypeComponent {
archetype_index,
component: TypeId::of::<T>(),
}
}
#[inline]
pub fn new_ty(archetype_index: u32, component: TypeId) -> Self {
ArchetypeComponent {
archetype_index,
component,
}
}
}
pub enum QueryAccess {
None,
Read(TypeId, &'static str),
Write(TypeId, &'static str),
Optional(Box<QueryAccess>),
With(TypeId, Box<QueryAccess>),
Without(TypeId, Box<QueryAccess>),
Union(Vec<QueryAccess>),
}
impl QueryAccess {
pub fn read<T: 'static>() -> QueryAccess {
QueryAccess::Read(TypeId::of::<T>(), std::any::type_name::<T>())
}
pub fn write<T: 'static>() -> QueryAccess {
QueryAccess::Write(TypeId::of::<T>(), std::any::type_name::<T>())
}
pub fn with<T: 'static>(access: QueryAccess) -> QueryAccess {
QueryAccess::With(TypeId::of::<T>(), Box::new(access))
}
pub fn without<T: 'static>(access: QueryAccess) -> QueryAccess {
QueryAccess::Without(TypeId::of::<T>(), Box::new(access))
}
pub fn optional(access: QueryAccess) -> QueryAccess {
QueryAccess::Optional(Box::new(access))
}
pub fn union(accesses: Vec<QueryAccess>) -> QueryAccess {
QueryAccess::Union(accesses)
}
pub fn get_world_archetype_access(
&self,
world: &World,
mut type_access: Option<&mut TypeAccess<ArchetypeComponent>>,
) {
let archetypes = world.archetypes();
for (i, archetype) in archetypes.enumerate() {
let type_access = type_access.as_deref_mut();
let _ = self.get_access(archetype, i as u32, type_access);
}
}
pub fn get_type_name(&self, type_id: TypeId) -> Option<&'static str> {
match self {
QueryAccess::None => None,
QueryAccess::Read(current_type_id, name) => {
if type_id == *current_type_id {
Some(*name)
} else {
None
}
}
QueryAccess::Write(current_type_id, name) => {
if type_id == *current_type_id {
Some(*name)
} else {
None
}
}
QueryAccess::Optional(query_access) => query_access.get_type_name(type_id),
QueryAccess::With(_, query_access) => query_access.get_type_name(type_id),
QueryAccess::Without(_, query_access) => query_access.get_type_name(type_id),
QueryAccess::Union(query_accesses) => {
for query_access in query_accesses.iter() {
if let Some(name) = query_access.get_type_name(type_id) {
return Some(name);
}
}
None
}
}
}
/// Returns how this [QueryAccess] accesses the given `archetype`.
/// If `type_access` is set, it will populate type access with the types this query reads/writes
pub fn get_access(
&self,
archetype: &Archetype,
archetype_index: u32,
type_access: Option<&mut TypeAccess<ArchetypeComponent>>,
) -> Option<Access> {
match self {
QueryAccess::None => Some(Access::None),
QueryAccess::Read(ty, _) => {
if archetype.has_type(*ty) {
if let Some(type_access) = type_access {
type_access.add_read(ArchetypeComponent::new_ty(archetype_index, *ty));
}
Some(Access::Read)
} else {
None
}
}
QueryAccess::Write(ty, _) => {
if archetype.has_type(*ty) {
if let Some(type_access) = type_access {
type_access.add_write(ArchetypeComponent::new_ty(archetype_index, *ty));
}
Some(Access::Write)
} else {
None
}
}
QueryAccess::Optional(query_access) => {
if let Some(access) = query_access.get_access(archetype, archetype_index, None) {
// only re-run get_archetype_access if we need to set type_access
if type_access.is_some() {
query_access.get_access(archetype, archetype_index, type_access)
} else {
Some(access)
}
} else {
Some(Access::Read)
}
}
QueryAccess::With(ty, query_access) => {
if archetype.has_type(*ty) {
query_access.get_access(archetype, archetype_index, type_access)
} else {
None
}
}
QueryAccess::Without(ty, query_access) => {
if !archetype.has_type(*ty) {
query_access.get_access(archetype, archetype_index, type_access)
} else {
None
}
}
QueryAccess::Union(query_accesses) => {
let mut result = None;
for query_access in query_accesses {
if let Some(access) = query_access.get_access(archetype, archetype_index, None)
{
result = Some(result.unwrap_or(Access::Read).max(access));
} else {
return None;
}
}
// only set the type access if there is a full match
if let Some(type_access) = type_access {
if result.is_some() {
for query_access in query_accesses {
query_access.get_access(archetype, archetype_index, Some(type_access));
}
}
}
result
}
}
}
}
/// Provides information about the types a [System] reads and writes
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct TypeAccess<T: Hash + Eq + PartialEq> {
reads_and_writes: HashSet<T>,
writes: HashSet<T>,
reads: HashSet<T>,
}
impl<T: Hash + Eq + PartialEq> Default for TypeAccess<T> {
fn default() -> Self {
Self {
reads_and_writes: Default::default(),
writes: Default::default(),
reads: Default::default(),
}
}
}
impl<T: Hash + Eq + PartialEq + Copy> TypeAccess<T> {
pub fn new(reads: Vec<T>, writes: Vec<T>) -> Self {
let mut type_access = TypeAccess::default();
for write in writes {
type_access.add_write(write);
}
for read in reads {
type_access.add_read(read);
}
type_access
}
pub fn is_compatible(&self, other: &TypeAccess<T>) -> bool {
self.writes.is_disjoint(&other.reads_and_writes)
&& self.reads_and_writes.is_disjoint(&other.writes)
}
pub fn get_conflict<'a>(&'a self, other: &'a TypeAccess<T>) -> Option<&'a T> {
let conflict = self.writes.intersection(&other.reads_and_writes).next();
if conflict.is_some() {
return conflict;
}
self.reads_and_writes.intersection(&other.writes).next()
}
pub fn union(&mut self, other: &TypeAccess<T>) {
self.writes.extend(&other.writes);
self.reads.extend(&other.reads);
self.reads_and_writes.extend(&other.reads_and_writes);
}
pub fn add_read(&mut self, ty: T) {
self.reads_and_writes.insert(ty);
self.reads.insert(ty);
}
pub fn add_write(&mut self, ty: T) {
self.reads_and_writes.insert(ty);
self.writes.insert(ty);
}
pub fn clear(&mut self) {
self.reads_and_writes.clear();
self.reads.clear();
self.writes.clear();
}
pub fn is_read_or_write(&self, ty: &T) -> bool {
self.reads_and_writes.contains(ty)
}
pub fn is_write(&self, ty: &T) -> bool {
self.writes.contains(ty)
}
pub fn iter_reads(&self) -> impl Iterator<Item = &T> {
self.reads.iter()
}
pub fn iter_writes(&self) -> impl Iterator<Item = &T> {
self.writes.iter()
}
}
#[cfg(test)]
mod tests {
use crate::{ArchetypeComponent, Entity, Fetch, Query, TypeAccess, With, World};
use std::vec;
struct A;
struct B;
struct C;
#[test]
fn query_type_access() {
let mut world = World::default();
let e1 = world.spawn((A,));
let e2 = world.spawn((A, B));
let e3 = world.spawn((A, B, C));
let e1_archetype = world.get_entity_location(e1).unwrap().archetype;
let e2_archetype = world.get_entity_location(e2).unwrap().archetype;
let e3_archetype = world.get_entity_location(e3).unwrap().archetype;
let e1_a = ArchetypeComponent::new::<A>(e1_archetype);
let e2_a = ArchetypeComponent::new::<A>(e2_archetype);
let e2_b = ArchetypeComponent::new::<B>(e2_archetype);
let e3_a = ArchetypeComponent::new::<A>(e3_archetype);
let e3_b = ArchetypeComponent::new::<B>(e3_archetype);
let e3_c = ArchetypeComponent::new::<C>(e3_archetype);
let mut a_type_access = TypeAccess::default();
<(&A,) as Query>::Fetch::access()
.get_world_archetype_access(&world, Some(&mut a_type_access));
assert_eq!(
a_type_access,
TypeAccess::new(vec![e1_a, e2_a, e3_a], vec![])
);
let mut a_b_type_access = TypeAccess::default();
<(&A, &B) as Query>::Fetch::access()
.get_world_archetype_access(&world, Some(&mut a_b_type_access));
assert_eq!(
a_b_type_access,
TypeAccess::new(vec![e2_a, e2_b, e3_a, e3_b], vec![])
);
let mut a_bmut_type_access = TypeAccess::default();
<(&A, &mut B) as Query>::Fetch::access()
.get_world_archetype_access(&world, Some(&mut a_bmut_type_access));
assert_eq!(
a_bmut_type_access,
TypeAccess::new(vec![e2_a, e3_a], vec![e2_b, e3_b])
);
let mut a_option_bmut_type_access = TypeAccess::default();
<(Entity, &A, Option<&mut B>) as Query>::Fetch::access()
.get_world_archetype_access(&world, Some(&mut a_option_bmut_type_access));
assert_eq!(
a_option_bmut_type_access,
TypeAccess::new(vec![e1_a, e2_a, e3_a], vec![e2_b, e3_b])
);
let mut a_with_b_type_access = TypeAccess::default();
<With<B, &A> as Query>::Fetch::access()
.get_world_archetype_access(&world, Some(&mut a_with_b_type_access));
assert_eq!(
a_with_b_type_access,
TypeAccess::new(vec![e2_a, e3_a], vec![])
);
let mut a_with_b_option_c_type_access = TypeAccess::default();
<With<B, (&A, Option<&mut C>)> as Query>::Fetch::access()
.get_world_archetype_access(&world, Some(&mut a_with_b_option_c_type_access));
assert_eq!(
a_with_b_option_c_type_access,
TypeAccess::new(vec![e2_a, e3_a], vec![e3_c])
);
}
}

View File

@ -31,7 +31,7 @@ use core::{
};
use std::collections::HashMap;
use crate::{borrow::AtomicBorrow, query::Fetch, Access, Component, Query};
use crate::{borrow::AtomicBorrow, Component};
/// A collection of entities having the same component types
///
@ -417,11 +417,6 @@ impl Archetype {
.cast::<u8>();
ptr::copy_nonoverlapping(component, ptr, size);
}
/// How, if at all, `Q` will access entities in this archetype
pub fn access<Q: Query>(&self) -> Option<Access> {
Q::Fetch::access(self)
}
}
impl Drop for Archetype {

View File

@ -40,7 +40,6 @@
//! assert_eq!(*world.get::<i32>(b).unwrap(), 42);
//! ```
#![warn(missing_docs)]
#![no_std]
#[cfg(feature = "std")]
@ -64,27 +63,27 @@ macro_rules! smaller_tuples_too {
};
}
mod access;
mod archetype;
mod borrow;
mod bundle;
mod entities;
mod entity_builder;
mod query;
mod query_one;
#[cfg(feature = "serde")]
mod serde;
mod world;
pub use access::{ArchetypeComponent, QueryAccess, TypeAccess};
pub use archetype::{Archetype, TypeState};
pub use borrow::{AtomicBorrow, Ref, RefMut};
pub use bundle::{Bundle, DynamicBundle, MissingComponent};
pub use entities::{Entity, EntityReserver, Location, NoSuchEntity};
pub use entity_builder::{BuiltEntity, EntityBuilder};
pub use query::{
Access, Added, BatchedIter, Changed, Mut, Mutated, Or, Query, QueryBorrow, QueryIter,
ReadOnlyFetch, With, Without,
Added, Batch, BatchedIter, Changed, Mut, Mutated, Or, Query, QueryIter, ReadOnlyFetch, With,
Without,
};
pub use query_one::QueryOne;
pub use world::{ArchetypesGeneration, Component, ComponentError, SpawnBatchIter, World};
// Unstable implementation details needed by the macros
@ -99,4 +98,4 @@ pub use lazy_static;
pub use query::Fetch;
#[cfg(feature = "macros")]
pub use bevy_hecs_macros::Bundle;
pub use bevy_hecs_macros::{impl_query_set, Bundle};

View File

@ -20,7 +20,9 @@ use core::{
ptr::NonNull,
};
use crate::{archetype::Archetype, Component, Entity, MissingComponent};
use std::vec;
use crate::{access::QueryAccess, archetype::Archetype, Component, Entity, MissingComponent};
/// A collection of component types to fetch from a `World`
pub trait Query {
@ -31,6 +33,9 @@ pub trait Query {
/// A fetch that is read only. This should only be implemented for read-only fetches.
pub unsafe trait ReadOnlyFetch {}
/// A fetch that will always match every entity in an archetype (aka Fetch::should_skip always returns false)
pub trait UnfilteredFetch {}
/// Streaming iterators over contiguous homogeneous ranges of components
pub trait Fetch<'a>: Sized {
/// Type of value to be fetched
@ -41,17 +46,13 @@ pub trait Fetch<'a>: Sized {
const DANGLING: Self;
/// How this query will access `archetype`, if at all
fn access(archetype: &Archetype) -> Option<Access>;
fn access() -> QueryAccess;
/// Acquire dynamic borrows from `archetype`
fn borrow(archetype: &Archetype);
/// Construct a `Fetch` for `archetype` if it should be traversed
///
/// # Safety
/// `offset` must be in bounds of `archetype`
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self>;
/// Release dynamic borrows acquired by `borrow`
fn release(archetype: &Archetype);
/// if this returns true, the nth item should be skipped during iteration
///
@ -71,20 +72,10 @@ pub trait Fetch<'a>: Sized {
unsafe fn fetch(&self, n: usize) -> Self::Item;
}
/// Type of access a `Query` may have to an `Archetype`
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum Access {
/// Read entity IDs only, no components
Iterate,
/// Read components
Read,
/// Read and write components
Write,
}
#[derive(Copy, Clone, Debug)]
pub struct EntityFetch(NonNull<Entity>);
unsafe impl ReadOnlyFetch for EntityFetch {}
impl UnfilteredFetch for EntityFetch {}
impl Query for Entity {
type Fetch = EntityFetch;
@ -95,14 +86,6 @@ impl<'a> Fetch<'a> for EntityFetch {
const DANGLING: Self = Self(NonNull::dangling());
#[inline]
fn access(_archetype: &Archetype) -> Option<Access> {
Some(Access::Iterate)
}
#[inline]
fn borrow(_archetype: &Archetype) {}
#[inline]
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
Some(EntityFetch(NonNull::new_unchecked(
@ -110,13 +93,15 @@ impl<'a> Fetch<'a> for EntityFetch {
)))
}
#[inline]
fn release(_archetype: &Archetype) {}
#[inline]
unsafe fn fetch(&self, n: usize) -> Self::Item {
*self.0.as_ptr().add(n)
}
#[inline]
fn access() -> QueryAccess {
QueryAccess::None
}
}
impl<'a, T: Component> Query for &'a T {
@ -127,38 +112,28 @@ impl<'a, T: Component> Query for &'a T {
pub struct FetchRead<T>(NonNull<T>);
unsafe impl<T> ReadOnlyFetch for FetchRead<T> {}
impl<T> UnfilteredFetch for FetchRead<T> {}
impl<'a, T: Component> Fetch<'a> for FetchRead<T> {
type Item = &'a T;
const DANGLING: Self = Self(NonNull::dangling());
fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() {
Some(Access::Read)
} else {
None
}
}
fn borrow(archetype: &Archetype) {
archetype.borrow::<T>();
}
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
archetype
.get::<T>()
.map(|x| Self(NonNull::new_unchecked(x.as_ptr().add(offset))))
}
fn release(archetype: &Archetype) {
archetype.release::<T>();
}
#[inline]
unsafe fn fetch(&self, n: usize) -> &'a T {
&*self.0.as_ptr().add(n)
}
#[inline]
fn access() -> QueryAccess {
QueryAccess::read::<T>()
}
}
impl<'a, T: Component> Query for &'a mut T {
@ -222,24 +197,13 @@ impl<'a, T: Component> Query for Mut<'a, T> {
}
#[doc(hidden)]
pub struct FetchMut<T>(NonNull<T>, NonNull<bool>);
impl<T> UnfilteredFetch for FetchMut<T> {}
impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
type Item = Mut<'a, T>;
const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling());
fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() {
Some(Access::Write)
} else {
None
}
}
fn borrow(archetype: &Archetype) {
archetype.borrow_mut::<T>();
}
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
archetype
.get_with_type_state::<T>()
@ -251,10 +215,6 @@ impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
})
}
fn release(archetype: &Archetype) {
archetype.release_mut::<T>();
}
#[inline]
unsafe fn fetch(&self, n: usize) -> Mut<'a, T> {
Mut {
@ -262,6 +222,11 @@ impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
mutated: &mut *self.1.as_ptr().add(n),
}
}
#[inline]
fn access() -> QueryAccess {
QueryAccess::write::<T>()
}
}
macro_rules! impl_or_query {
@ -275,30 +240,17 @@ macro_rules! impl_or_query {
const DANGLING: Self = Self(($( $T::DANGLING ),+));
fn access(archetype: &Archetype) -> Option<Access> {
let mut max_access = None;
$(
max_access = max_access.max($T::access(archetype));
)+
max_access
fn access() -> QueryAccess {
QueryAccess::union(vec![
$($T::access(),)+
])
}
fn borrow(archetype: &Archetype) {
$(
$T::borrow(archetype);
)+
}
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
Some(Self(( $( $T::get(archetype, offset)?),+ )))
}
fn release(archetype: &Archetype) {
$(
$T::release(archetype);
)+
}
#[allow(non_snake_case)]
unsafe fn fetch(&self, n: usize) -> Self::Item {
let ($( $T ),+) = &self.0;
@ -332,12 +284,11 @@ impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10);
/// let mut world = World::new();
/// world.spawn((123, true, 1., Some(1)));
/// world.spawn((456, false, 2., Some(0)));
/// for mut b in world.query_mut::<Mut<i32>>().iter().skip(1).take(1) {
/// for mut b in world.query_mut::<Mut<i32>>().skip(1).take(1) {
/// *b += 1;
/// }
/// let components = world
/// .query_mut::<Or<(Mutated<bool>, Mutated<i32>, Mutated<f64>, Mutated<Option<i32>>)>>()
/// .iter()
/// .map(|(b, i, f, o)| (*b, *i))
/// .collect::<Vec<_>>();
/// assert_eq!(components, &[(false, 457)]);
@ -375,16 +326,9 @@ impl<'a, T: Component> Fetch<'a> for FetchMutated<T> {
const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling());
fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() {
Some(Access::Read)
} else {
None
}
}
fn borrow(archetype: &Archetype) {
archetype.borrow::<T>();
#[inline]
fn access() -> QueryAccess {
QueryAccess::read::<T>()
}
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
@ -398,10 +342,6 @@ impl<'a, T: Component> Fetch<'a> for FetchMutated<T> {
})
}
fn release(archetype: &Archetype) {
archetype.release::<T>();
}
unsafe fn should_skip(&self, n: usize) -> bool {
// skip if the current item wasn't mutated
!*self.1.as_ptr().add(n)
@ -442,16 +382,9 @@ impl<'a, T: Component> Fetch<'a> for FetchAdded<T> {
const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling());
fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() {
Some(Access::Read)
} else {
None
}
}
fn borrow(archetype: &Archetype) {
archetype.borrow::<T>();
#[inline]
fn access() -> QueryAccess {
QueryAccess::read::<T>()
}
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
@ -465,10 +398,6 @@ impl<'a, T: Component> Fetch<'a> for FetchAdded<T> {
})
}
fn release(archetype: &Archetype) {
archetype.release::<T>();
}
unsafe fn should_skip(&self, n: usize) -> bool {
// skip if the current item wasn't added
!*self.1.as_ptr().add(n)
@ -513,16 +442,9 @@ impl<'a, T: Component> Fetch<'a> for FetchChanged<T> {
NonNull::dangling(),
);
fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() {
Some(Access::Read)
} else {
None
}
}
fn borrow(archetype: &Archetype) {
archetype.borrow::<T>();
#[inline]
fn access() -> QueryAccess {
QueryAccess::read::<T>()
}
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
@ -537,10 +459,6 @@ impl<'a, T: Component> Fetch<'a> for FetchChanged<T> {
})
}
fn release(archetype: &Archetype) {
archetype.release::<T>();
}
unsafe fn should_skip(&self, n: usize) -> bool {
// skip if the current item wasn't added or mutated
!*self.1.as_ptr().add(n) && !*self.2.as_ptr().add(n)
@ -557,28 +475,22 @@ impl<'a, T: Component> Fetch<'a> for FetchChanged<T> {
#[doc(hidden)]
pub struct TryFetch<T>(Option<T>);
unsafe impl<T> ReadOnlyFetch for TryFetch<T> where T: ReadOnlyFetch {}
impl<T> UnfilteredFetch for TryFetch<T> where T: UnfilteredFetch {}
impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
type Item = Option<T::Item>;
const DANGLING: Self = Self(None);
fn access(archetype: &Archetype) -> Option<Access> {
Some(T::access(archetype).unwrap_or(Access::Iterate))
}
fn borrow(archetype: &Archetype) {
T::borrow(archetype)
#[inline]
fn access() -> QueryAccess {
QueryAccess::optional(T::access())
}
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
Some(Self(T::get(archetype, offset)))
}
fn release(archetype: &Archetype) {
T::release(archetype)
}
unsafe fn fetch(&self, n: usize) -> Option<T::Item> {
Some(self.0.as_ref()?.fetch(n))
}
@ -600,7 +512,6 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
/// let b = world.spawn((456, false));
/// let c = world.spawn((42, "def"));
/// let entities = world.query::<Without<bool, (Entity, &i32)>>()
/// .iter()
/// .map(|(e, &i)| (e, i))
/// .collect::<Vec<_>>();
/// assert_eq!(entities, &[(c, 42)]);
@ -617,22 +528,16 @@ unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWithout<T, F>
F: ReadOnlyFetch
{
}
impl<T, F> UnfilteredFetch for FetchWithout<T, F> where F: UnfilteredFetch {}
impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout<T, F> {
type Item = F::Item;
const DANGLING: Self = Self(F::DANGLING, PhantomData);
fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() {
None
} else {
F::access(archetype)
}
}
fn borrow(archetype: &Archetype) {
F::borrow(archetype)
#[inline]
fn access() -> QueryAccess {
QueryAccess::without::<T>(F::access())
}
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
@ -642,10 +547,6 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout<T, F> {
Some(Self(F::get(archetype, offset)?, PhantomData))
}
fn release(archetype: &Archetype) {
F::release(archetype)
}
unsafe fn fetch(&self, n: usize) -> F::Item {
self.0.fetch(n)
}
@ -667,7 +568,6 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout<T, F> {
/// let b = world.spawn((456, false));
/// let c = world.spawn((42, "def"));
/// let entities = world.query::<With<bool, (Entity, &i32)>>()
/// .iter()
/// .map(|(e, &i)| (e, i))
/// .collect::<Vec<_>>();
/// assert_eq!(entities.len(), 2);
@ -683,22 +583,16 @@ impl<T: Component, Q: Query> Query for With<T, Q> {
#[doc(hidden)]
pub struct FetchWith<T, F>(F, PhantomData<fn(T)>);
unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWith<T, F> where F: ReadOnlyFetch {}
impl<T, F> UnfilteredFetch for FetchWith<T, F> where F: UnfilteredFetch {}
impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith<T, F> {
type Item = F::Item;
const DANGLING: Self = Self(F::DANGLING, PhantomData);
fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() {
F::access(archetype)
} else {
None
}
}
fn borrow(archetype: &Archetype) {
F::borrow(archetype)
#[inline]
fn access() -> QueryAccess {
QueryAccess::with::<T>(F::access())
}
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
@ -708,10 +602,6 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith<T, F> {
Some(Self(F::get(archetype, offset)?, PhantomData))
}
fn release(archetype: &Archetype) {
F::release(archetype)
}
unsafe fn fetch(&self, n: usize) -> F::Item {
self.0.fetch(n)
}
@ -721,179 +611,86 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith<T, F> {
}
}
/// A borrow of a `World` sufficient to execute the query `Q`
///
/// Note that borrows are not released until this object is dropped.
pub struct QueryBorrow<'w, Q: Query> {
archetypes: &'w [Archetype],
borrowed: bool,
_marker: PhantomData<Q>,
}
impl<'w, Q: Query> QueryBorrow<'w, Q> {
pub(crate) fn new(archetypes: &'w [Archetype]) -> Self {
Self {
archetypes,
borrowed: false,
_marker: PhantomData,
}
}
/// Execute the query
///
/// Must be called only once per query.
pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, Q> {
self.borrow();
QueryIter {
borrow: self,
archetype_index: 0,
iter: ChunkIter::EMPTY,
}
}
/// Like `iter`, but returns child iterators of at most `batch_size` elements
///
/// Useful for distributing work over a threadpool.
pub fn iter_batched<'q>(&'q mut self, batch_size: usize) -> BatchedIter<'q, 'w, Q> {
self.borrow();
BatchedIter {
borrow: self,
archetype_index: 0,
batch_size,
batch: 0,
}
}
fn borrow(&mut self) {
if self.borrowed {
panic!(
"called QueryBorrow::iter twice on the same borrow; construct a new query instead"
);
}
self.borrowed = true;
}
/// Transform the query into one that requires a certain component without borrowing it
///
/// This can be useful when the component needs to be borrowed elsewhere and it isn't necessary
/// for the iterator to expose its data directly.
///
/// Equivalent to using a query type wrapped in `With`.
///
/// # Example
/// ```
/// # use bevy_hecs::*;
/// let mut world = World::new();
/// let a = world.spawn((123, true, "abc"));
/// let b = world.spawn((456, false));
/// let c = world.spawn((42, "def"));
/// let entities = world.query::<(Entity, &i32)>()
/// .with::<bool>()
/// .iter()
/// .map(|(e, &i)| (e, i)) // Copy out of the world
/// .collect::<Vec<_>>();
/// assert!(entities.contains(&(a, 123)));
/// assert!(entities.contains(&(b, 456)));
/// ```
pub fn with<T: Component>(self) -> QueryBorrow<'w, With<T, Q>> {
self.transform()
}
/// Transform the query into one that skips entities having a certain component
///
/// Equivalent to using a query type wrapped in `Without`.
///
/// # Example
/// ```
/// # use bevy_hecs::*;
/// let mut world = World::new();
/// let a = world.spawn((123, true, "abc"));
/// let b = world.spawn((456, false));
/// let c = world.spawn((42, "def"));
/// let entities = world.query::<(Entity, &i32)>()
/// .without::<bool>()
/// .iter()
/// .map(|(e, &i)| (e, i)) // Copy out of the world
/// .collect::<Vec<_>>();
/// assert_eq!(entities, &[(c, 42)]);
/// ```
pub fn without<T: Component>(self) -> QueryBorrow<'w, Without<T, Q>> {
self.transform()
}
/// Helper to change the type of the query
fn transform<R: Query>(mut self) -> QueryBorrow<'w, R> {
let borrow = QueryBorrow {
archetypes: self.archetypes,
borrowed: self.borrowed,
_marker: PhantomData,
};
self.borrowed = false;
borrow
}
}
unsafe impl<'w, Q: Query> Send for QueryBorrow<'w, Q> {}
unsafe impl<'w, Q: Query> Sync for QueryBorrow<'w, Q> {}
impl<'q, 'w, Q: Query> IntoIterator for &'q mut QueryBorrow<'w, Q> {
type IntoIter = QueryIter<'q, 'w, Q>;
type Item = <Q::Fetch as Fetch<'q>>::Item;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
struct ChunkInfo<Q: Query> {
fetch: Q::Fetch,
len: usize,
}
/// Iterator over the set of entities with the components in `Q`
pub struct QueryIter<'q, 'w, Q: Query> {
borrow: &'q mut QueryBorrow<'w, Q>,
pub struct QueryIter<'w, Q: Query> {
archetypes: &'w [Archetype],
archetype_index: usize,
iter: ChunkIter<Q>,
chunk_info: ChunkInfo<Q>,
chunk_position: usize,
}
unsafe impl<'q, 'w, Q: Query> Send for QueryIter<'q, 'w, Q> {}
unsafe impl<'q, 'w, Q: Query> Sync for QueryIter<'q, 'w, Q> {}
impl<'w, Q: Query> QueryIter<'w, Q> {
// #[allow(clippy::declare_interior_mutable_const)] // no trait bounds on const fns
// const EMPTY: Q::Fetch = Q::Fetch::DANGLING;
const EMPTY: ChunkInfo<Q> = ChunkInfo {
fetch: Q::Fetch::DANGLING,
len: 0,
};
impl<'q, 'w, Q: Query> Iterator for QueryIter<'q, 'w, Q> {
type Item = <Q::Fetch as Fetch<'q>>::Item;
/// Creates a new QueryIter
#[inline]
pub(crate) fn new(archetypes: &'w [Archetype]) -> Self {
Self {
archetypes,
archetype_index: 0,
chunk_info: Self::EMPTY,
chunk_position: 0,
}
}
}
impl<'w, Q: Query> Iterator for QueryIter<'w, Q> {
type Item = <Q::Fetch as Fetch<'w>>::Item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
loop {
match unsafe { self.iter.next() } {
None => {
let archetype = self.borrow.archetypes.get(self.archetype_index)?;
unsafe {
loop {
if self.chunk_position == self.chunk_info.len {
let archetype = self.archetypes.get(self.archetype_index)?;
self.archetype_index += 1;
unsafe {
self.iter = Q::Fetch::get(archetype, 0).map_or(ChunkIter::EMPTY, |fetch| {
ChunkIter {
fetch,
len: archetype.len(),
position: 0,
}
});
}
self.chunk_position = 0;
self.chunk_info = Q::Fetch::get(archetype, 0)
.map(|fetch| ChunkInfo {
fetch,
len: archetype.len(),
})
.unwrap_or(Self::EMPTY);
continue;
}
Some(components) => return Some(components),
if self
.chunk_info
.fetch
.should_skip(self.chunk_position as usize)
{
self.chunk_position += 1;
continue;
}
let item = Some(self.chunk_info.fetch.fetch(self.chunk_position as usize));
self.chunk_position += 1;
return item;
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.len();
(n, Some(n))
}
}
impl<'q, 'w, Q: Query> ExactSizeIterator for QueryIter<'q, 'w, Q> {
// if the Fetch is an UnfilteredFetch, then we can cheaply compute the length of the query by getting
// the length of each matching archetype
impl<'w, Q: Query> ExactSizeIterator for QueryIter<'w, Q>
where
Q::Fetch: UnfilteredFetch,
{
fn len(&self) -> usize {
self.borrow
.archetypes
self.archetypes
.iter()
.filter(|&x| Q::Fetch::access(x).is_some())
.filter(|&archetype| unsafe { Q::Fetch::get(archetype, 0).is_some() })
.map(|x| x.len())
.sum()
}
@ -906,13 +703,6 @@ struct ChunkIter<Q: Query> {
}
impl<Q: Query> ChunkIter<Q> {
#[allow(clippy::declare_interior_mutable_const)] // no trait bounds on const fns
const EMPTY: Self = Self {
fetch: Q::Fetch::DANGLING,
position: 0,
len: 0,
};
unsafe fn next<'a>(&mut self) -> Option<<Q::Fetch as Fetch<'a>>::Item> {
loop {
if self.position == self.len {
@ -932,22 +722,35 @@ impl<Q: Query> ChunkIter<Q> {
}
/// Batched version of `QueryIter`
pub struct BatchedIter<'q, 'w, Q: Query> {
borrow: &'q mut QueryBorrow<'w, Q>,
pub struct BatchedIter<'w, Q: Query> {
archetypes: &'w [Archetype],
archetype_index: usize,
batch_size: usize,
batch: usize,
_marker: PhantomData<Q>,
}
unsafe impl<'q, 'w, Q: Query> Send for BatchedIter<'q, 'w, Q> {}
unsafe impl<'q, 'w, Q: Query> Sync for BatchedIter<'q, 'w, Q> {}
impl<'w, Q: Query> BatchedIter<'w, Q> {
pub(crate) fn new(archetypes: &'w [Archetype], batch_size: usize) -> Self {
Self {
archetypes,
archetype_index: 0,
batch_size,
batch: 0,
_marker: Default::default(),
}
}
}
impl<'q, 'w, Q: Query> Iterator for BatchedIter<'q, 'w, Q> {
type Item = Batch<'q, Q>;
unsafe impl<'w, Q: Query> Send for BatchedIter<'w, Q> {}
unsafe impl<'w, Q: Query> Sync for BatchedIter<'w, Q> {}
impl<'w, Q: Query> Iterator for BatchedIter<'w, Q> {
type Item = Batch<'w, Q>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let archetype = self.borrow.archetypes.get(self.archetype_index)?;
let archetype = self.archetypes.get(self.archetype_index)?;
let offset = self.batch_size * self.batch;
if offset >= archetype.len() {
self.archetype_index += 1;
@ -1001,26 +804,16 @@ macro_rules! tuple_impl {
const DANGLING: Self = ($($name::DANGLING,)*);
#[allow(unused_variables, unused_mut)]
fn access(archetype: &Archetype) -> Option<Access> {
let mut access = Access::Iterate;
$(
access = access.max($name::access(archetype)?);
)*
Some(access)
fn access() -> QueryAccess {
QueryAccess::union(vec![
$($name::access(),)*
])
}
#[allow(unused_variables)]
fn borrow(archetype: &Archetype) {
$($name::borrow(archetype);)*
}
#[allow(unused_variables)]
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
Some(($($name::get(archetype, offset)?,)*))
}
#[allow(unused_variables)]
fn release(archetype: &Archetype) {
$($name::release(archetype);)*
}
#[allow(unused_variables)]
unsafe fn fetch(&self, n: usize) -> Self::Item {
@ -1042,6 +835,7 @@ macro_rules! tuple_impl {
}
unsafe impl<$($name: ReadOnlyFetch),*> ReadOnlyFetch for ($($name,)*) {}
impl<$($name: UnfilteredFetch),*> UnfilteredFetch for ($($name,)*) {}
};
}
@ -1054,13 +848,6 @@ mod tests {
use super::*;
#[test]
fn access_order() {
assert!(Access::Write > Access::Read);
assert!(Access::Read > Access::Iterate);
assert!(Some(Access::Iterate) > None);
}
struct A(usize);
struct B(usize);
struct C;
@ -1073,7 +860,6 @@ mod tests {
fn get_added<Com: Component>(world: &World) -> Vec<Entity> {
world
.query::<(Added<Com>, Entity)>()
.iter()
.map(|(_added, e)| e)
.collect::<Vec<Entity>>()
};
@ -1091,7 +877,6 @@ mod tests {
let added = world
.query::<(Entity, Added<A>, Added<B>)>()
.iter()
.map(|a| a.0)
.collect::<Vec<Entity>>();
assert_eq!(added, vec![e2]);
@ -1105,7 +890,7 @@ mod tests {
let e3 = world.spawn((A(0), B(0)));
world.spawn((A(0), B));
for (i, mut a) in world.query_mut::<Mut<A>>().iter().enumerate() {
for (i, mut a) in world.query_mut::<Mut<A>>().enumerate() {
if i % 2 == 0 {
a.0 += 1;
}
@ -1114,7 +899,6 @@ mod tests {
fn get_changed_a(world: &mut World) -> Vec<Entity> {
world
.query_mut::<(Mutated<A>, Entity)>()
.iter()
.map(|(_a, e)| e)
.collect::<Vec<Entity>>()
};
@ -1154,7 +938,6 @@ mod tests {
assert!(world
.query_mut::<(Mutated<A>, Entity)>()
.iter()
.map(|(_a, e)| e)
.collect::<Vec<Entity>>()
.is_empty());
@ -1167,17 +950,16 @@ mod tests {
let e2 = world.spawn((A(0), B(0)));
world.spawn((A(0), B(0)));
for mut a in world.query_mut::<Mut<A>>().iter() {
for mut a in world.query_mut::<Mut<A>>() {
a.0 += 1;
}
for mut b in world.query_mut::<Mut<B>>().iter().skip(1).take(1) {
for mut b in world.query_mut::<Mut<B>>().skip(1).take(1) {
b.0 += 1;
}
let a_b_changed = world
.query_mut::<(Mutated<A>, Mutated<B>, Entity)>()
.iter()
.map(|(_a, _b, e)| e)
.collect::<Vec<Entity>>();
assert_eq!(a_b_changed, vec![e2]);
@ -1192,17 +974,16 @@ mod tests {
let _e4 = world.spawn((A(0), B(0)));
// Mutate A in entities e1 and e2
for mut a in world.query_mut::<Mut<A>>().iter().take(2) {
for mut a in world.query_mut::<Mut<A>>().take(2) {
a.0 += 1;
}
// Mutate B in entities e2 and e3
for mut b in world.query_mut::<Mut<B>>().iter().skip(1).take(2) {
for mut b in world.query_mut::<Mut<B>>().skip(1).take(2) {
b.0 += 1;
}
let a_b_changed = world
.query_mut::<(Or<(Mutated<A>, Mutated<B>)>, Entity)>()
.iter()
.map(|((_a, _b), e)| e)
.collect::<Vec<Entity>>();
// e1 has mutated A, e3 has mutated B, e2 has mutated A and B, _e4 has no mutated component
@ -1217,7 +998,6 @@ mod tests {
fn get_changed(world: &World) -> Vec<Entity> {
world
.query::<(Changed<A>, Entity)>()
.iter()
.map(|(_a, e)| e)
.collect::<Vec<Entity>>()
};
@ -1227,4 +1007,16 @@ mod tests {
*world.get_mut(e1).unwrap() = A(1);
assert_eq!(get_changed(&world), vec![e1]);
}
#[test]
fn exact_size_query() {
let mut world = World::default();
world.spawn((A(0), B(0)));
world.spawn((A(0), B(0)));
world.spawn((C,));
assert_eq!(world.query::<(&A, &B)>().len(), 2);
// the following example shouldn't compile because Changed<A> is not an UnfilteredFetch
// assert_eq!(world.query::<(Changed<A>, &B)>().len(), 2);
}
}

View File

@ -1,144 +0,0 @@
// modified by Bevy contributors
use core::marker::PhantomData;
use crate::{
query::{Fetch, ReadOnlyFetch, With, Without},
Archetype, Component, Query,
};
/// A borrow of a `World` sufficient to execute the query `Q` on a single entity
pub struct QueryOne<'a, Q: Query> {
archetype: &'a Archetype,
index: usize,
_marker: PhantomData<Q>,
}
impl<'a, Q: Query> QueryOne<'a, Q> {
/// Construct a query accessing the entity in `archetype` at `index`
///
/// # Safety
///
/// `index` must be in-bounds for `archetype`
pub(crate) unsafe fn new(archetype: &'a Archetype, index: usize) -> Self {
Self {
archetype,
index,
_marker: PhantomData,
}
}
/// Get the query result, or `None` if the entity does not satisfy the query
///
/// Must be called at most once.
///
/// Panics if called more than once or if it would construct a borrow that clashes with another
/// pre-existing borrow.
pub fn get(&mut self) -> Option<<Q::Fetch as Fetch<'_>>::Item> {
unsafe {
let fetch = Q::Fetch::get(self.archetype, self.index)?;
if fetch.should_skip(0) {
None
} else {
Some(fetch.fetch(0))
}
}
}
/// Transform the query into one that requires a certain component without borrowing it
///
/// See `QueryBorrow::with` for details.
pub fn with<T: Component>(self) -> QueryOne<'a, With<T, Q>> {
self.transform()
}
/// Transform the query into one that skips entities having a certain component
///
/// See `QueryBorrow::without` for details.
pub fn without<T: Component>(self) -> QueryOne<'a, Without<T, Q>> {
self.transform()
}
/// Helper to change the type of the query
fn transform<R: Query>(self) -> QueryOne<'a, R> {
QueryOne {
archetype: self.archetype,
index: self.index,
_marker: PhantomData,
}
}
}
unsafe impl<Q: Query> Send for QueryOne<'_, Q> {}
unsafe impl<Q: Query> Sync for QueryOne<'_, Q> {}
/// A read only borrow of a `World` sufficient to execute the query `Q` on a single entity
pub struct ReadOnlyQueryOne<'a, Q: Query> {
archetype: &'a Archetype,
index: usize,
_marker: PhantomData<Q>,
}
impl<'a, Q: Query> ReadOnlyQueryOne<'a, Q>
where
Q::Fetch: ReadOnlyFetch,
{
/// Construct a query accessing the entity in `archetype` at `index`
///
/// # Safety
///
/// `index` must be in-bounds for `archetype`
pub(crate) unsafe fn new(archetype: &'a Archetype, index: usize) -> Self {
Self {
archetype,
index,
_marker: PhantomData,
}
}
/// Get the query result, or `None` if the entity does not satisfy the query
///
/// Must be called at most once.
///
/// Panics if called more than once or if it would construct a borrow that clashes with another
/// pre-existing borrow.
pub fn get(&self) -> Option<<Q::Fetch as Fetch<'_>>::Item>
where
Q::Fetch: ReadOnlyFetch,
{
unsafe {
let fetch = Q::Fetch::get(self.archetype, self.index)?;
if fetch.should_skip(0) {
None
} else {
Some(fetch.fetch(0))
}
}
}
/// Transform the query into one that requires a certain component without borrowing it
///
/// See `QueryBorrow::with` for details.
pub fn with<T: Component>(self) -> QueryOne<'a, With<T, Q>> {
self.transform()
}
/// Transform the query into one that skips entities having a certain component
///
/// See `QueryBorrow::without` for details.
pub fn without<T: Component>(self) -> QueryOne<'a, Without<T, Q>> {
self.transform()
}
/// Helper to change the type of the query
fn transform<R: Query>(self) -> QueryOne<'a, R> {
QueryOne {
archetype: self.archetype,
index: self.index,
_marker: PhantomData,
}
}
}
unsafe impl<Q: Query> Send for ReadOnlyQueryOne<'_, Q> {}
unsafe impl<Q: Query> Sync for ReadOnlyQueryOne<'_, Q> {}

View File

@ -15,8 +15,8 @@
// modified by Bevy contributors
use crate::{
alloc::vec::Vec, borrow::EntityRef, query::ReadOnlyFetch, query_one::ReadOnlyQueryOne,
EntityReserver, Mut, RefMut,
alloc::vec::Vec, borrow::EntityRef, query::ReadOnlyFetch, BatchedIter, EntityReserver, Fetch,
Mut, QueryIter, RefMut,
};
use bevy_utils::{HashMap, HashSet};
use core::{any::TypeId, fmt, mem, ptr};
@ -27,8 +27,7 @@ use std::error::Error;
use crate::{
archetype::Archetype,
entities::{Entities, Location},
Bundle, DynamicBundle, Entity, MissingComponent, NoSuchEntity, Query, QueryBorrow, QueryOne,
Ref,
Bundle, DynamicBundle, Entity, MissingComponent, NoSuchEntity, Query, Ref,
};
/// An unordered collection of entities, each having any number of distinctly typed components
@ -236,9 +235,6 @@ impl World {
///
/// Entities are yielded in arbitrary order.
///
/// The returned `QueryBorrow` can be further transformed with combinator methods; see its
/// documentation for details.
///
/// # Example
/// ```
/// # use bevy_hecs::*;
@ -247,14 +243,13 @@ impl World {
/// let b = world.spawn((456, false));
/// let c = world.spawn((42, "def"));
/// let entities = world.query::<(Entity, &i32, &bool)>()
/// .iter()
/// .map(|(e, &i, &b)| (e, i, b)) // Copy out of the world
/// .collect::<Vec<_>>();
/// assert_eq!(entities.len(), 2);
/// assert!(entities.contains(&(a, 123, true)));
/// assert!(entities.contains(&(b, 456, false)));
/// ```
pub fn query<Q: Query>(&self) -> QueryBorrow<'_, Q>
pub fn query<Q: Query>(&self) -> QueryIter<'_, Q>
where
Q::Fetch: ReadOnlyFetch,
{
@ -272,9 +267,6 @@ impl World {
///
/// Entities are yielded in arbitrary order.
///
/// The returned `QueryBorrow` can be further transformed with combinator methods; see its
/// documentation for details.
///
/// # Example
/// ```
/// # use bevy_hecs::*;
@ -282,19 +274,35 @@ impl World {
/// let a = world.spawn((123, true, "abc"));
/// let b = world.spawn((456, false));
/// let c = world.spawn((42, "def"));
/// let entities = world.query::<(Entity, &i32, &bool)>()
/// .iter()
/// .map(|(e, &i, &b)| (e, i, b)) // Copy out of the world
/// let entities = world.query_mut::<(Entity, &mut i32, &bool)>()
/// .map(|(e, i, &b)| (e, *i, b)) // Copy out of the world
/// .collect::<Vec<_>>();
/// assert_eq!(entities.len(), 2);
/// assert!(entities.contains(&(a, 123, true)));
/// assert!(entities.contains(&(b, 456, false)));
/// ```
pub fn query_mut<Q: Query>(&mut self) -> QueryBorrow<'_, Q> {
pub fn query_mut<Q: Query>(&mut self) -> QueryIter<'_, Q> {
// SAFE: unique mutable access
unsafe { self.query_unchecked() }
}
/// Like `query`, but instead of returning a single iterator it returns a "batched iterator",
/// where each batch is `batch_size`. This is generally used for parallel iteration.
pub fn query_batched<Q: Query>(&self, batch_size: usize) -> BatchedIter<'_, Q>
where
Q::Fetch: ReadOnlyFetch,
{
// SAFE: read-only access to world and read only query prevents mutable access
unsafe { self.query_batched_unchecked(batch_size) }
}
/// Like `query`, but instead of returning a single iterator it returns a "batched iterator",
/// where each batch is `batch_size`. This is generally used for parallel iteration.
pub fn query_batched_mut<Q: Query>(&mut self, batch_size: usize) -> BatchedIter<'_, Q> {
// SAFE: unique mutable access
unsafe { self.query_batched_unchecked(batch_size) }
}
/// Efficiently iterate over all entities that have certain components
///
/// Calling `iter` on the returned value yields `(Entity, Q)` tuples, where `Q` is some query
@ -305,36 +313,29 @@ impl World {
///
/// Entities are yielded in arbitrary order.
///
/// The returned `QueryBorrow` can be further transformed with combinator methods; see its
/// documentation for details.
/// # Safety
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
pub unsafe fn query_unchecked<Q: Query>(&self) -> QueryIter<'_, Q> {
QueryIter::new(&self.archetypes)
}
/// Like `query`, but instead of returning a single iterator it returns a "batched iterator",
/// where each batch is `batch_size`. This is generally used for parallel iteration.
///
/// # Safety
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
///
/// # Example
/// ```
/// # use bevy_hecs::*;
/// let mut world = World::new();
/// let a = world.spawn((123, true, "abc"));
/// let b = world.spawn((456, false));
/// let c = world.spawn((42, "def"));
/// let entities = world.query::<(Entity, &i32, &bool)>()
/// .iter()
/// .map(|(e, &i, &b)| (e, i, b)) // Copy out of the world
/// .collect::<Vec<_>>();
/// assert_eq!(entities.len(), 2);
/// assert!(entities.contains(&(a, 123, true)));
/// assert!(entities.contains(&(b, 456, false)));
/// ```
pub unsafe fn query_unchecked<Q: Query>(&self) -> QueryBorrow<'_, Q> {
QueryBorrow::new(&self.archetypes)
#[inline]
pub unsafe fn query_batched_unchecked<Q: Query>(
&self,
batch_size: usize,
) -> BatchedIter<'_, Q> {
BatchedIter::new(&self.archetypes, batch_size)
}
/// Prepare a read only query against a single entity
///
/// Call `get` on the resulting `QueryOne` to actually execute the query.
///
/// Handy for accessing multiple components simultaneously.
///
/// # Example
@ -343,25 +344,22 @@ impl World {
/// let mut world = World::new();
/// let a = world.spawn((123, true, "abc"));
/// // The returned query must outlive the borrow made by `get`
/// let mut query = world.query_one::<(&i32, &bool)>(a).unwrap();
/// let (number, flag) = query.get().unwrap();
/// let (number, flag) = world.query_one::<(&i32, &bool)>(a).unwrap();
/// assert_eq!(*number, 123);
/// ```
pub fn query_one<Q: Query>(
&self,
entity: Entity,
) -> Result<ReadOnlyQueryOne<'_, Q>, NoSuchEntity>
) -> Result<<Q::Fetch as Fetch>::Item, NoSuchEntity>
where
Q::Fetch: ReadOnlyFetch,
{
let loc = self.entities.get(entity)?;
Ok(unsafe { ReadOnlyQueryOne::new(&self.archetypes[loc.archetype as usize], loc.index) })
// SAFE: read-only access to world and read only query prevents mutable access
unsafe { self.query_one_unchecked::<Q>(entity) }
}
/// Prepare a query against a single entity
///
/// Call `get` on the resulting `QueryOne` to actually execute the query.
///
/// Handy for accessing multiple components simultaneously.
///
/// # Example
@ -370,49 +368,34 @@ impl World {
/// let mut world = World::new();
/// let a = world.spawn((123, true, "abc"));
/// // The returned query must outlive the borrow made by `get`
/// let mut query = world.query_one_mut::<(&mut i32, &bool)>(a).unwrap();
/// let (mut number, flag) = query.get().unwrap();
/// let (mut number, flag) = world.query_one_mut::<(&mut i32, &bool)>(a).unwrap();
/// if *flag { *number *= 2; }
/// assert_eq!(*number, 246);
/// ```
pub fn query_one_mut<Q: Query>(
&mut self,
entity: Entity,
) -> Result<QueryOne<'_, Q>, NoSuchEntity> {
) -> Result<<Q::Fetch as Fetch>::Item, NoSuchEntity> {
// SAFE: unique mutable access to world
unsafe { self.query_one_mut_unchecked(entity) }
unsafe { self.query_one_unchecked::<Q>(entity) }
}
/// Prepare a query against a single entity, without checking the safety of mutable queries
///
/// Call `get` on the resulting `QueryOne` to actually execute the query.
///
/// Handy for accessing multiple components simultaneously.
///
/// # Safety
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
///
/// # Example
/// ```
/// # use bevy_hecs::*;
/// let mut world = World::new();
/// let a = world.spawn((123, true, "abc"));
/// // The returned query must outlive the borrow made by `get`
/// let mut query = world.query_one_mut::<(&mut i32, &bool)>(a).unwrap();
/// let (mut number, flag) = query.get().unwrap();
/// if *flag { *number *= 2; }
/// assert_eq!(*number, 246);
/// ```
pub unsafe fn query_one_mut_unchecked<Q: Query>(
pub unsafe fn query_one_unchecked<Q: Query>(
&self,
entity: Entity,
) -> Result<QueryOne<'_, Q>, NoSuchEntity> {
) -> Result<<Q::Fetch as Fetch>::Item, NoSuchEntity> {
let loc = self.entities.get(entity)?;
Ok(QueryOne::new(
&self.archetypes[loc.archetype as usize],
loc.index,
))
<Q::Fetch as Fetch>::get(&self.archetypes[loc.archetype as usize], 0)
.filter(|fetch| !fetch.should_skip(loc.index))
.map(|fetch| fetch.fetch(loc.index))
.ok_or(NoSuchEntity)
}
/// Borrow the `T` component of `entity`

View File

@ -34,9 +34,9 @@ fn despawn() {
let mut world = World::new();
let e = world.spawn(("abc", 123));
let f = world.spawn(("def", 456));
assert_eq!(world.query::<()>().iter().count(), 2);
assert_eq!(world.query::<()>().count(), 2);
world.despawn(e).unwrap();
assert_eq!(world.query::<()>().iter().count(), 1);
assert_eq!(world.query::<()>().count(), 1);
assert!(world.get::<&str>(e).is_err());
assert!(world.get::<i32>(e).is_err());
assert_eq!(*world.get::<&str>(f).unwrap(), "def");
@ -51,14 +51,13 @@ fn query_all() {
let ents = world
.query::<(Entity, &i32, &&str)>()
.iter()
.map(|(e, &i, &s)| (e, i, s))
.collect::<Vec<_>>();
assert_eq!(ents.len(), 2);
assert!(ents.contains(&(e, 123, "abc")));
assert!(ents.contains(&(f, 456, "def")));
let ents = world.query::<Entity>().iter().collect::<Vec<_>>();
let ents = world.query::<Entity>().collect::<Vec<_>>();
assert_eq!(ents.len(), 2);
assert!(ents.contains(&e));
assert!(ents.contains(&f));
@ -71,7 +70,6 @@ fn query_single_component() {
let f = world.spawn(("def", 456, true));
let ents = world
.query::<(Entity, &i32)>()
.iter()
.map(|(e, &i)| (e, i))
.collect::<Vec<_>>();
assert_eq!(ents.len(), 2);
@ -84,7 +82,7 @@ fn query_missing_component() {
let mut world = World::new();
world.spawn(("abc", 123));
world.spawn(("def", 456));
assert!(world.query::<(&bool, &i32)>().iter().next().is_none());
assert!(world.query::<(&bool, &i32)>().next().is_none());
}
#[test]
@ -94,7 +92,6 @@ fn query_sparse_component() {
let f = world.spawn(("def", 456, true));
let ents = world
.query::<(Entity, &bool)>()
.iter()
.map(|(e, &b)| (e, b))
.collect::<Vec<_>>();
assert_eq!(ents, &[(f, true)]);
@ -107,7 +104,6 @@ fn query_optional_component() {
let f = world.spawn(("def", 456, true));
let ents = world
.query::<(Entity, Option<&bool>, &i32)>()
.iter()
.map(|(e, b, &i)| (e, b.copied(), i))
.collect::<Vec<_>>();
assert_eq!(ents.len(), 2);
@ -140,7 +136,6 @@ fn dynamic_components() {
assert_eq!(
world
.query::<(Entity, &i32, &bool)>()
.iter()
.map(|(e, &i, &b)| (e, i, b))
.collect::<Vec<_>>(),
&[(e, 42, true)]
@ -149,7 +144,6 @@ fn dynamic_components() {
assert_eq!(
world
.query::<(Entity, &i32, &bool)>()
.iter()
.map(|(e, &i, &b)| (e, i, b))
.collect::<Vec<_>>(),
&[]
@ -157,7 +151,6 @@ fn dynamic_components() {
assert_eq!(
world
.query::<(Entity, &bool, &&str)>()
.iter()
.map(|(e, &b, &s)| (e, b, s))
.collect::<Vec<_>>(),
&[(e, true, "abc")]
@ -222,17 +215,6 @@ fn clear() {
assert_eq!(world.iter().count(), 0);
}
#[test]
#[should_panic(expected = "twice on the same borrow")]
fn alias() {
let mut world = World::new();
world.spawn(("abc", 123));
world.spawn(("def", 456, true));
let mut q = world.query_mut::<Entity>();
let _a = q.iter().collect::<Vec<_>>();
let _b = q.iter().collect::<Vec<_>>();
}
#[test]
fn remove_missing() {
let mut world = World::new();
@ -246,22 +228,15 @@ fn query_batched() {
let a = world.spawn(());
let b = world.spawn(());
let c = world.spawn((42,));
assert_eq!(world.query::<()>().iter_batched(1).count(), 3);
assert_eq!(world.query::<()>().iter_batched(2).count(), 2);
assert_eq!(
world.query::<()>().iter_batched(2).flat_map(|x| x).count(),
3
);
assert_eq!(world.query_batched::<()>(1).count(), 3);
assert_eq!(world.query_batched::<()>(2).count(), 2);
assert_eq!(world.query_batched::<()>(2).flat_map(|x| x).count(), 3);
// different archetypes are always in different batches
assert_eq!(world.query::<()>().iter_batched(3).count(), 2);
assert_eq!(
world.query::<()>().iter_batched(3).flat_map(|x| x).count(),
3
);
assert_eq!(world.query::<()>().iter_batched(4).count(), 2);
assert_eq!(world.query_batched::<()>(3).count(), 2);
assert_eq!(world.query_batched::<()>(3).flat_map(|x| x).count(), 3);
assert_eq!(world.query_batched::<()>(4).count(), 2);
let entities = world
.query::<Entity>()
.iter_batched(1)
.query_batched::<Entity>(1)
.flat_map(|x| x)
.map(|e| e)
.collect::<Vec<_>>();
@ -276,7 +251,7 @@ fn query_batched() {
fn spawn_batch() {
let mut world = World::new();
world.spawn_batch((0..100).map(|x| (x, "abc")));
let entities = world.query::<&i32>().iter().map(|&x| x).collect::<Vec<_>>();
let entities = world.query::<&i32>().map(|&x| x).collect::<Vec<_>>();
assert_eq!(entities.len(), 100);
}
@ -286,13 +261,10 @@ fn query_one() {
let a = world.spawn(("abc", 123));
let b = world.spawn(("def", 456));
let c = world.spawn(("ghi", 789, true));
assert_eq!(world.query_one::<&i32>(a).unwrap().get(), Some(&123));
assert_eq!(world.query_one::<&i32>(b).unwrap().get(), Some(&456));
assert!(world.query_one::<(&i32, &bool)>(a).unwrap().get().is_none());
assert_eq!(
world.query_one::<(&i32, &bool)>(c).unwrap().get(),
Some((&789, &true))
);
assert_eq!(world.query_one::<&i32>(a), Ok(&123));
assert_eq!(world.query_one::<&i32>(b), Ok(&456));
assert!(world.query_one::<(&i32, &bool)>(a).is_err());
assert_eq!(world.query_one::<(&i32, &bool)>(c), Ok((&789, &true)));
world.despawn(a).unwrap();
assert!(world.query_one::<&i32>(a).is_err());
}
@ -371,29 +343,21 @@ fn added_tracking() {
let mut world = World::new();
let a = world.spawn((123,));
assert_eq!(world.query::<&i32>().iter().count(), 1);
assert_eq!(world.query::<Added<i32>>().iter().count(), 1);
assert_eq!(world.query_mut::<&i32>().iter().count(), 1);
assert_eq!(world.query_mut::<Added<i32>>().iter().count(), 1);
assert!(world.query_one::<&i32>(a).unwrap().get().is_some());
assert!(world.query_one::<Added<i32>>(a).unwrap().get().is_some());
assert!(world.query_one_mut::<&i32>(a).unwrap().get().is_some());
assert!(world
.query_one_mut::<Added<i32>>(a)
.unwrap()
.get()
.is_some());
assert_eq!(world.query::<&i32>().count(), 1);
assert_eq!(world.query::<Added<i32>>().count(), 1);
assert_eq!(world.query_mut::<&i32>().count(), 1);
assert_eq!(world.query_mut::<Added<i32>>().count(), 1);
assert!(world.query_one::<&i32>(a).is_ok());
assert!(world.query_one::<Added<i32>>(a).is_ok());
assert!(world.query_one_mut::<&i32>(a).is_ok());
assert!(world.query_one_mut::<Added<i32>>(a).is_ok());
world.clear_trackers();
assert_eq!(world.query::<&i32>().iter().count(), 1);
assert_eq!(world.query::<Added<i32>>().iter().count(), 0);
assert_eq!(world.query_mut::<&i32>().iter().count(), 1);
assert_eq!(world.query_mut::<Added<i32>>().iter().count(), 0);
assert!(world.query_one_mut::<&i32>(a).unwrap().get().is_some());
assert!(world
.query_one_mut::<Added<i32>>(a)
.unwrap()
.get()
.is_none());
assert_eq!(world.query::<&i32>().count(), 1);
assert_eq!(world.query::<Added<i32>>().count(), 0);
assert_eq!(world.query_mut::<&i32>().count(), 1);
assert_eq!(world.query_mut::<Added<i32>>().count(), 0);
assert!(world.query_one_mut::<&i32>(a).is_ok());
assert!(world.query_one_mut::<Added<i32>>(a).is_err());
}

View File

@ -16,7 +16,7 @@ pub mod prelude {
Commands, IntoForEachSystem, IntoQuerySystem, IntoThreadLocalSystem, Query, System,
},
world::WorldBuilderSource,
Added, Bundle, Changed, Component, Entity, Mut, Mutated, Or, Ref, RefMut, With, Without,
World,
Added, Bundle, Changed, Component, Entity, Mut, Mutated, Or, QuerySet, Ref, RefMut, With,
Without, World,
};
}

View File

@ -1,15 +1,13 @@
use super::{FromResources, Resources};
use crate::{
system::{SystemId, TypeAccess},
Resource, ResourceIndex,
};
use bevy_hecs::smaller_tuples_too;
use crate::{system::SystemId, Resource, ResourceIndex};
use bevy_hecs::{smaller_tuples_too, TypeAccess};
use core::{
any::TypeId,
ops::{Deref, DerefMut},
ptr::NonNull,
};
use std::marker::PhantomData;
use std::{any::TypeId, marker::PhantomData};
// TODO: align TypeAccess api with Query::Fetch
/// A shared borrow of a Resource
/// that will only return in a query if the Resource has been changed
@ -183,7 +181,7 @@ pub trait FetchResource<'a>: Sized {
/// Type of value to be fetched
type Item: UnsafeClone;
fn access() -> TypeAccess;
fn access() -> TypeAccess<TypeId>;
fn borrow(resources: &Resources);
fn release(resources: &Resources);
@ -219,9 +217,9 @@ impl<'a, T: Resource> FetchResource<'a> for FetchResourceRead<T> {
resources.release::<T>();
}
fn access() -> TypeAccess {
fn access() -> TypeAccess<TypeId> {
let mut access = TypeAccess::default();
access.immutable.insert(TypeId::of::<T>());
access.add_read(TypeId::of::<T>());
access
}
}
@ -254,9 +252,9 @@ impl<'a, T: Resource> FetchResource<'a> for FetchResourceChanged<T> {
resources.release::<T>();
}
fn access() -> TypeAccess {
fn access() -> TypeAccess<TypeId> {
let mut access = TypeAccess::default();
access.immutable.insert(TypeId::of::<T>());
access.add_read(TypeId::of::<T>());
access
}
}
@ -286,9 +284,9 @@ impl<'a, T: Resource> FetchResource<'a> for FetchResourceWrite<T> {
resources.release_mut::<T>();
}
fn access() -> TypeAccess {
fn access() -> TypeAccess<TypeId> {
let mut access = TypeAccess::default();
access.mutable.insert(TypeId::of::<T>());
access.add_write(TypeId::of::<T>());
access
}
}
@ -328,9 +326,9 @@ impl<'a, T: Resource + FromResources> FetchResource<'a> for FetchResourceLocalMu
resources.release_mut::<T>();
}
fn access() -> TypeAccess {
fn access() -> TypeAccess<TypeId> {
let mut access = TypeAccess::default();
access.mutable.insert(TypeId::of::<T>());
access.add_write(TypeId::of::<T>());
access
}
}
@ -361,7 +359,7 @@ macro_rules! tuple_impl {
}
#[allow(unused_mut)]
fn access() -> TypeAccess {
fn access() -> TypeAccess<TypeId> {
let mut access = TypeAccess::default();
$(access.union(&$name::access());)*
access
@ -422,7 +420,7 @@ macro_rules! tuple_impl_or {
}
#[allow(unused_mut)]
fn access() -> TypeAccess {
fn access() -> TypeAccess<TypeId> {
let mut access = TypeAccess::default();
$(access.union(&$name::access());)*
access

View File

@ -1,9 +1,9 @@
use super::Schedule;
use crate::{
resource::Resources,
system::{ArchetypeAccess, System, ThreadLocalExecution, TypeAccess},
system::{System, ThreadLocalExecution},
};
use bevy_hecs::{ArchetypesGeneration, World};
use bevy_hecs::{ArchetypesGeneration, TypeAccess, World};
use bevy_tasks::{ComputeTaskPool, CountdownEvent, TaskPool};
use fixedbitset::FixedBitSet;
use std::ops::Range;
@ -11,8 +11,8 @@ use std::ops::Range;
/// Executes each schedule stage in parallel by analyzing system dependencies.
/// System execution order is undefined except under the following conditions:
/// * systems in earlier stages run before systems in later stages
/// * in a given stage, systems that mutate archetype X cannot run before systems registered before them that read/write archetype X
/// * in a given stage, systems the read archetype X cannot run before systems registered before them that write archetype X
/// * in a given stage, systems that mutate [archetype+component] X cannot run before systems registered before them that read/write [archetype+component] X
/// * in a given stage, systems the read [archetype+component] X cannot run before systems registered before them that write [archetype+component] X
/// * in a given stage, systems that mutate resource Y cannot run before systems registered before them that read/write resource Y
/// * in a given stage, systems the read resource Y cannot run before systems registered before them that write resource Y
@ -70,6 +70,28 @@ impl ParallelExecutor {
self.last_schedule_generation = schedule_generation;
}
pub fn print_order(&self, schedule: &Schedule) {
println!("----------------------------");
for (stage_name, executor_stage) in schedule.stage_order.iter().zip(self.stages.iter()) {
println!("stage {:?}", stage_name);
if let Some(stage_systems) = schedule.stages.get(stage_name) {
for (i, system) in stage_systems.iter().enumerate() {
println!(" {}-{}", i, system.name());
println!(
" dependencies({:?})",
executor_stage.system_dependencies[i]
.ones()
.collect::<Vec<usize>>()
);
println!(
" dependants({:?})",
executor_stage.system_dependents[i]
);
}
}
}
}
}
#[derive(Debug, Clone)]
@ -146,9 +168,9 @@ impl ExecutorStage {
self.last_archetypes_generation != world.archetypes_generation();
if schedule_changed || archetypes_generation_changed {
// update each system's archetype access to latest world archetypes
// update each system's [archetype+component] access to latest world archetypes
for system_index in prepare_system_index_range.clone() {
systems[system_index].update_archetype_access(world);
systems[system_index].update(world);
// Clear this so that the next block of code that populates it doesn't insert
// duplicates
@ -157,11 +179,11 @@ impl ExecutorStage {
}
// calculate dependencies between systems and build execution order
let mut current_archetype_access = ArchetypeAccess::default();
let mut current_archetype_access = TypeAccess::default();
let mut current_resource_access = TypeAccess::default();
for system_index in prepare_system_index_range.clone() {
let system = &systems[system_index];
let archetype_access = system.archetype_access();
let archetype_access = system.archetype_component_access();
match system.thread_local_execution() {
ThreadLocalExecution::NextFlush => {
let resource_access = system.resource_access();
@ -183,7 +205,7 @@ impl ExecutorStage {
// if earlier system is incompatible, make the current system dependent
if !earlier_system
.archetype_access()
.archetype_component_access()
.is_compatible(archetype_access)
|| !earlier_system
.resource_access()
@ -538,14 +560,14 @@ mod tests {
commands.spawn((1u32,));
}
fn read(query: Query<&u32>, mut entities: Query<Entity>) {
fn read(query: Query<&u32>, entities: Query<Entity>) {
for entity in &mut entities.iter() {
// query.get() does a "system permission check" that will fail if the entity is from a
// new archetype which hasnt been "prepared yet"
query.get::<u32>(entity).unwrap();
}
assert_eq!(1, entities.iter().iter().count());
assert_eq!(1, entities.iter().count());
}
schedule.add_system_to_stage("PreArchetypeChange", insert.system());
@ -569,14 +591,14 @@ mod tests {
world.spawn((1u32,));
}
fn read(query: Query<&u32>, mut entities: Query<Entity>) {
fn read(query: Query<&u32>, entities: Query<Entity>) {
for entity in &mut entities.iter() {
// query.get() does a "system permission check" that will fail if the entity is from a
// new archetype which hasnt been "prepared yet"
query.get::<u32>(entity).unwrap();
}
assert_eq!(1, entities.iter().iter().count());
assert_eq!(1, entities.iter().count());
}
schedule.add_system_to_stage("update", insert.thread_local_system());
@ -625,7 +647,6 @@ mod tests {
fn read_u32(completed_systems: Res<CompletedSystems>, _query: Query<&u32>) {
let mut completed_systems = completed_systems.completed_systems.lock();
assert!(!completed_systems.contains(READ_U32_WRITE_U64_SYSTEM_NAME));
completed_systems.insert(READ_U32_SYSTEM_NAME);
}
@ -639,7 +660,6 @@ mod tests {
_query: Query<(&u32, &mut u64)>,
) {
let mut completed_systems = completed_systems.completed_systems.lock();
assert!(completed_systems.contains(READ_U32_SYSTEM_NAME));
assert!(!completed_systems.contains(READ_U64_SYSTEM_NAME));
completed_systems.insert(READ_U32_WRITE_U64_SYSTEM_NAME);
}
@ -734,7 +754,7 @@ mod tests {
assert_eq!(
executor.stages[0].system_dependents,
vec![vec![2], vec![], vec![3], vec![]]
vec![vec![], vec![], vec![3], vec![]]
);
assert_eq!(
executor.stages[1].system_dependents,
@ -746,8 +766,6 @@ mod tests {
);
let stage_0_len = executor.stages[0].system_dependencies.len();
let mut read_u32_write_u64_deps = FixedBitSet::with_capacity(stage_0_len);
read_u32_write_u64_deps.insert(0);
let mut read_u64_deps = FixedBitSet::with_capacity(stage_0_len);
read_u64_deps.insert(2);
@ -756,7 +774,7 @@ mod tests {
vec![
FixedBitSet::with_capacity(stage_0_len),
FixedBitSet::with_capacity(stage_0_len),
read_u32_write_u64_deps,
FixedBitSet::with_capacity(stage_0_len),
read_u64_deps,
]
);

View File

@ -150,7 +150,7 @@ impl Schedule {
for system in stage_systems.iter_mut() {
#[cfg(feature = "profiler")]
crate::profiler_start(resources, system.name().clone());
system.update_archetype_access(world);
system.update(world);
match system.thread_local_execution() {
ThreadLocalExecution::NextFlush => system.run(world, resources),
ThreadLocalExecution::Immediate => {

View File

@ -207,7 +207,7 @@ impl CommandsInternal {
}
}
/// A queue of [Command]s to run on the current [World] and [Resources]
/// A queue of [Command]s to run on the current [World] and [Resources]. Todo: remove arc here
#[derive(Default, Clone)]
pub struct Commands {
pub commands: Arc<Mutex<CommandsInternal>>,
@ -357,7 +357,6 @@ mod tests {
command_buffer.apply(&mut world, &mut resources);
let results = world
.query::<(&u32, &u64)>()
.iter()
.map(|(a, b)| (*a, *b))
.collect::<Vec<_>>();
assert_eq!(results, vec![(1u32, 2u64)]);
@ -368,7 +367,6 @@ mod tests {
command_buffer.apply(&mut world, &mut resources);
let results2 = world
.query::<(&u32, &u64)>()
.iter()
.map(|(a, b)| (*a, *b))
.collect::<Vec<_>>();
assert_eq!(results2, vec![]);

View File

@ -1,19 +1,19 @@
pub use super::Query;
use super::TypeAccess;
use crate::{
resource::{FetchResource, ResourceQuery, Resources, UnsafeClone},
system::{ArchetypeAccess, Commands, System, SystemId, ThreadLocalExecution},
system::{Commands, System, SystemId, ThreadLocalExecution},
QueryAccess, QuerySet, QueryTuple, TypeAccess,
};
use bevy_hecs::{Fetch, Query as HecsQuery, World};
use std::borrow::Cow;
use bevy_hecs::{ArchetypeComponent, Fetch, Query as HecsQuery, World};
use std::{any::TypeId, borrow::Cow};
#[derive(Debug)]
pub(crate) struct SystemFn<State, F, ThreadLocalF, Init, SetArchetypeAccess>
pub(crate) struct SystemFn<State, F, ThreadLocalF, Init, Update>
where
F: FnMut(&World, &Resources, &ArchetypeAccess, &mut State) + Send + Sync,
F: FnMut(&World, &Resources, &mut State) + Send + Sync,
ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync,
Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync,
SetArchetypeAccess: FnMut(&World, &mut ArchetypeAccess, &mut State) + Send + Sync,
Update: FnMut(&World, &mut TypeAccess<ArchetypeComponent>, &mut State) + Send + Sync,
State: Send + Sync,
{
pub state: State,
@ -21,35 +21,34 @@ where
pub thread_local_func: ThreadLocalF,
pub init_func: Init,
pub thread_local_execution: ThreadLocalExecution,
pub resource_access: TypeAccess,
pub resource_access: TypeAccess<TypeId>,
pub name: Cow<'static, str>,
pub id: SystemId,
pub archetype_access: ArchetypeAccess,
pub set_archetype_access: SetArchetypeAccess,
pub archetype_component_access: TypeAccess<ArchetypeComponent>,
pub update_func: Update,
}
impl<State, F, ThreadLocalF, Init, SetArchetypeAccess> System
for SystemFn<State, F, ThreadLocalF, Init, SetArchetypeAccess>
impl<State, F, ThreadLocalF, Init, Update> System for SystemFn<State, F, ThreadLocalF, Init, Update>
where
F: FnMut(&World, &Resources, &ArchetypeAccess, &mut State) + Send + Sync,
F: FnMut(&World, &Resources, &mut State) + Send + Sync,
ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync,
Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync,
SetArchetypeAccess: FnMut(&World, &mut ArchetypeAccess, &mut State) + Send + Sync,
Update: FnMut(&World, &mut TypeAccess<ArchetypeComponent>, &mut State) + Send + Sync,
State: Send + Sync,
{
fn name(&self) -> Cow<'static, str> {
self.name.clone()
}
fn update_archetype_access(&mut self, world: &World) {
(self.set_archetype_access)(world, &mut self.archetype_access, &mut self.state);
fn update(&mut self, world: &World) {
(self.update_func)(world, &mut self.archetype_component_access, &mut self.state);
}
fn archetype_access(&self) -> &ArchetypeAccess {
&self.archetype_access
fn archetype_component_access(&self) -> &TypeAccess<ArchetypeComponent> {
&self.archetype_component_access
}
fn resource_access(&self) -> &TypeAccess {
fn resource_access(&self) -> &TypeAccess<TypeId> {
&self.resource_access
}
@ -59,7 +58,7 @@ where
#[inline]
fn run(&mut self, world: &World, resources: &Resources) {
(self.func)(world, resources, &self.archetype_access, &mut self.state);
(self.func)(world, resources, &mut self.state);
}
fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources) {
@ -80,6 +79,11 @@ pub trait IntoForEachSystem<CommandBuffer, R, C> {
fn system(self) -> Box<dyn System>;
}
struct ForEachState {
commands: Commands,
query_access: QueryAccess,
}
macro_rules! impl_into_foreach_system {
(($($commands: ident)*), ($($resource: ident),*), ($($component: ident),*)) => {
impl<Func, $($resource,)* $($component,)*> IntoForEachSystem<($($commands,)*), ($($resource,)*), ($($component,)*)> for Func
@ -100,34 +104,38 @@ macro_rules! impl_into_foreach_system {
fn system(mut self) -> Box<dyn System> {
let id = SystemId::new();
Box::new(SystemFn {
state: Commands::default(),
state: ForEachState {
commands: Commands::default(),
query_access: <($($component,)*) as HecsQuery>::Fetch::access(),
},
thread_local_execution: ThreadLocalExecution::NextFlush,
name: core::any::type_name::<Self>().into(),
id,
func: move |world, resources, _archetype_access, state| {
func: move |world, resources, state| {
{
let state_commands = &state.commands;
if let Some(($($resource,)*)) = resources.query_system::<($($resource,)*)>(id) {
// SAFE: the scheduler has ensured that there is no archetype clashing here
unsafe {
for ($($component,)*) in world.query_unchecked::<($($component,)*)>().iter() {
fn_call!(self, ($($commands, state)*), ($($resource),*), ($($component),*))
for ($($component,)*) in world.query_unchecked::<($($component,)*)>() {
fn_call!(self, ($($commands, state_commands)*), ($($resource),*), ($($component),*))
}
}
}
}
},
thread_local_func: move |world, resources, state| {
state.apply(world, resources);
state.commands.apply(world, resources);
},
init_func: move |world, resources, state| {
<($($resource,)*)>::initialize(resources, Some(id));
state.set_entity_reserver(world.get_entity_reserver())
state.commands.set_entity_reserver(world.get_entity_reserver())
},
resource_access: <<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::access(),
archetype_access: ArchetypeAccess::default(),
set_archetype_access: |world, archetype_access, _state| {
archetype_access.clear();
archetype_access.set_access_for_query::<($($component,)*)>(world);
archetype_component_access: TypeAccess::default(),
update_func: |world, archetype_component_access, state| {
archetype_component_access.clear();
state.query_access.get_world_archetype_access(world, Some(archetype_component_access));
},
})
}
@ -136,26 +144,31 @@ macro_rules! impl_into_foreach_system {
}
struct QuerySystemState {
archetype_accesses: Vec<ArchetypeAccess>,
query_accesses: Vec<Vec<QueryAccess>>,
query_type_names: Vec<&'static str>,
archetype_component_accesses: Vec<TypeAccess<ArchetypeComponent>>,
commands: Commands,
}
/// Converts `Self` into a Query System
pub trait IntoQuerySystem<Commands, R, Q> {
pub trait IntoQuerySystem<Commands, R, Q, QS> {
fn system(self) -> Box<dyn System>;
}
macro_rules! impl_into_query_system {
(($($commands: ident)*), ($($resource: ident),*), ($($query: ident),*)) => {
impl<Func, $($resource,)* $($query,)*> IntoQuerySystem<($($commands,)*), ($($resource,)*), ($($query,)*)> for Func where
(($($commands: ident)*), ($($resource: ident),*), ($($query: ident),*), ($($query_set: ident),*)) => {
impl<Func, $($resource,)* $($query,)* $($query_set,)*> IntoQuerySystem<($($commands,)*), ($($resource,)*), ($($query,)*), ($($query_set,)*)> for Func where
Func:
FnMut($($commands,)* $($resource,)* $(Query<$query>,)*) +
FnMut($($commands,)* $($resource,)* $(Query<$query>,)* $(QuerySet<$query_set>,)*) +
FnMut(
$($commands,)*
$(<<$resource as ResourceQuery>::Fetch as FetchResource>::Item,)*
$(Query<$query>,)*) +
$(Query<$query>,)*
$(QuerySet<$query_set>,)*
) +
Send + Sync +'static,
$($query: HecsQuery,)*
$($query_set: QueryTuple,)*
$($resource: ResourceQuery,)*
{
#[allow(non_snake_case)]
@ -165,28 +178,46 @@ macro_rules! impl_into_query_system {
#[allow(unused_mut)]
fn system(mut self) -> Box<dyn System> {
let id = SystemId::new();
$(let $query = ArchetypeAccess::default();)*
let query_accesses = vec![
$(vec![<$query::Fetch as Fetch>::access()],)*
$($query_set::get_accesses(),)*
];
let query_type_names = vec![
$(std::any::type_name::<$query>(),)*
$(std::any::type_name::<$query_set>(),)*
];
let archetype_component_accesses = vec![TypeAccess::default(); query_accesses.len()];
Box::new(SystemFn {
state: QuerySystemState {
archetype_accesses: vec![
$($query,)*
],
query_accesses,
query_type_names,
archetype_component_accesses,
commands: Commands::default(),
},
thread_local_execution: ThreadLocalExecution::NextFlush,
id,
name: core::any::type_name::<Self>().into(),
func: move |world, resources, archetype_access, state| {
func: move |world, resources, state| {
{
if let Some(($($resource,)*)) = resources.query_system::<($($resource,)*)>(id) {
let mut i = 0;
$(
let $query = Query::<$query>::new(world, &state.archetype_accesses[i]);
let $query = Query::<$query>::new(
world,
&state.archetype_component_accesses[i]
);
i += 1;
)*
$(
let $query_set = QuerySet::<$query_set>::new(
world,
&state.archetype_component_accesses[i]
);
i += 1;
)*
let commands = &state.commands;
fn_call!(self, ($($commands, commands)*), ($($resource),*), ($($query),*))
fn_call!(self, ($($commands, commands)*), ($($resource),*), ($($query),*), ($($query_set),*))
}
}
},
@ -199,18 +230,40 @@ macro_rules! impl_into_query_system {
},
resource_access: <<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::access(),
archetype_access: ArchetypeAccess::default(),
set_archetype_access: |world, archetype_access, state| {
archetype_access.clear();
let mut i = 0;
let mut access: &mut ArchetypeAccess;
$(
access = &mut state.archetype_accesses[i];
access.clear();
access.set_access_for_query::<$query>(world);
archetype_access.union(access);
i += 1;
)*
archetype_component_access: TypeAccess::default(),
update_func: |world, archetype_component_access, state| {
archetype_component_access.clear();
let mut conflict_index = None;
let mut conflict_name = None;
for (i, (query_accesses, component_access)) in state.query_accesses.iter().zip(state.archetype_component_accesses.iter_mut()).enumerate() {
component_access.clear();
for query_access in query_accesses.iter() {
query_access.get_world_archetype_access(world, Some(component_access));
}
if !component_access.is_compatible(archetype_component_access) {
conflict_index = Some(i);
conflict_name = component_access.get_conflict(archetype_component_access).and_then(|archetype_component|
query_accesses
.iter()
.filter_map(|query_access| query_access.get_type_name(archetype_component.component))
.next());
break;
}
archetype_component_access.union(component_access);
}
if let Some(conflict_index) = conflict_index {
let mut conflicts_with_index = None;
for prior_index in 0..conflict_index {
if !state.archetype_component_accesses[conflict_index].is_compatible(&state.archetype_component_accesses[prior_index]) {
conflicts_with_index = Some(prior_index);
}
}
panic!("System {} has conflicting queries. {} conflicts with the component access [{}] in this prior query: {}",
core::any::type_name::<Self>(),
state.query_type_names[conflict_index],
conflict_name.unwrap_or("Unknown"),
conflicts_with_index.map(|index| state.query_type_names[index]).unwrap_or("Unknown"));
}
},
})
}
@ -219,6 +272,9 @@ macro_rules! impl_into_query_system {
}
macro_rules! fn_call {
($self:ident, ($($commands: ident, $commands_var: ident)*), ($($resource: ident),*), ($($a: ident),*), ($($b: ident),*)) => {
unsafe { $self($($commands_var.clone(),)* $($resource.unsafe_clone(),)* $($a,)* $($b,)*) }
};
($self:ident, ($($commands: ident, $commands_var: ident)*), ($($resource: ident),*), ($($a: ident),*)) => {
unsafe { $self($($commands_var.clone(),)* $($resource.unsafe_clone(),)* $($a,)*) }
};
@ -230,9 +286,18 @@ macro_rules! fn_call {
macro_rules! impl_into_query_systems {
(($($resource: ident,)*), ($($query: ident),*)) => {
#[rustfmt::skip]
impl_into_query_system!((), ($($resource),*), ($($query),*));
impl_into_query_system!((), ($($resource),*), ($($query),*), ());
#[rustfmt::skip]
impl_into_query_system!((Commands), ($($resource),*), ($($query),*));
impl_into_query_system!((), ($($resource),*), ($($query),*), (QS1));
#[rustfmt::skip]
impl_into_query_system!((), ($($resource),*), ($($query),*), (QS1, QS2));
#[rustfmt::skip]
impl_into_query_system!((Commands), ($($resource),*), ($($query),*), ());
#[rustfmt::skip]
impl_into_query_system!((Commands), ($($resource),*), ($($query),*), (QS1));
#[rustfmt::skip]
impl_into_query_system!((Commands), ($($resource),*), ($($query),*), (QS1, QS2));
}
}
@ -319,14 +384,14 @@ where
thread_local_func: move |world, resources, _| {
self.run(world, resources);
},
func: |_, _, _, _| {},
func: |_, _, _| {},
init_func: |_, _, _| {},
set_archetype_access: |_, _, _| {},
update_func: |_, _, _| {},
thread_local_execution: ThreadLocalExecution::Immediate,
name: core::any::type_name::<F>().into(),
id: SystemId::new(),
resource_access: TypeAccess::default(),
archetype_access: ArchetypeAccess::default(),
archetype_component_access: TypeAccess::default(),
})
}
}
@ -351,10 +416,11 @@ mod tests {
use crate::{
resource::{ResMut, Resources},
schedule::Schedule,
ChangedRes, Mut,
ChangedRes, Mut, QuerySet,
};
use bevy_hecs::{Entity, With, World};
#[derive(Debug, Eq, PartialEq)]
struct A;
struct B;
struct C;
@ -364,12 +430,12 @@ mod tests {
fn query_system_gets() {
fn query_system(
mut ran: ResMut<bool>,
mut entity_query: Query<With<A, Entity>>,
entity_query: Query<With<A, Entity>>,
b_query: Query<&B>,
a_c_query: Query<(&A, &C)>,
d_query: Query<&D>,
) {
let entities = entity_query.iter().iter().collect::<Vec<Entity>>();
let entities = entity_query.iter().collect::<Vec<Entity>>();
assert!(
b_query.get::<B>(entities[0]).is_err(),
"entity 0 should not have B"
@ -379,8 +445,9 @@ mod tests {
"entity 1 should have B"
);
assert!(
b_query.get::<A>(entities[1]).is_ok(),
"entity 1 should have A, and it should (unintuitively) be accessible from b_query because b_query grabs read access to the (A,B) archetype");
b_query.get::<A>(entities[1]).is_err(),
"entity 1 should have A, but b_query shouldn't have access to it"
);
assert!(
b_query.get::<D>(entities[3]).is_err(),
"entity 3 should have D, but it shouldn't be accessible from b_query"
@ -447,4 +514,83 @@ mod tests {
schedule.run(&mut world, &mut resources);
assert_eq!(*(world.get::<i32>(ent).unwrap()), 2);
}
#[test]
#[should_panic]
fn conflicting_query_mut_system() {
fn sys(_q1: Query<&mut A>, _q2: Query<&mut A>) {}
let mut world = World::default();
let mut resources = Resources::default();
world.spawn((A,));
let mut schedule = Schedule::default();
schedule.add_stage("update");
schedule.add_system_to_stage("update", sys.system());
schedule.run(&mut world, &mut resources);
}
#[test]
#[should_panic]
fn conflicting_query_immut_system() {
fn sys(_q1: Query<&A>, _q2: Query<&mut A>) {}
let mut world = World::default();
let mut resources = Resources::default();
world.spawn((A,));
let mut schedule = Schedule::default();
schedule.add_stage("update");
schedule.add_system_to_stage("update", sys.system());
schedule.run(&mut world, &mut resources);
}
#[test]
fn query_set_system() {
fn sys(_set: QuerySet<(Query<&mut A>, Query<&B>)>) {}
let mut world = World::default();
let mut resources = Resources::default();
world.spawn((A,));
let mut schedule = Schedule::default();
schedule.add_stage("update");
schedule.add_system_to_stage("update", sys.system());
schedule.run(&mut world, &mut resources);
}
#[test]
#[should_panic]
fn conflicting_query_with_query_set_system() {
fn sys(_query: Query<&mut A>, _set: QuerySet<(Query<&mut A>, Query<&B>)>) {}
let mut world = World::default();
let mut resources = Resources::default();
world.spawn((A,));
let mut schedule = Schedule::default();
schedule.add_stage("update");
schedule.add_system_to_stage("update", sys.system());
schedule.run(&mut world, &mut resources);
}
#[test]
#[should_panic]
fn conflicting_query_sets_system() {
fn sys(_set_1: QuerySet<(Query<&mut A>,)>, _set_2: QuerySet<(Query<&mut A>, Query<&B>)>) {}
let mut world = World::default();
let mut resources = Resources::default();
world.spawn((A,));
let mut schedule = Schedule::default();
schedule.add_stage("update");
schedule.add_system_to_stage("update", sys.system());
schedule.run(&mut world, &mut resources);
}
}

View File

@ -1,451 +0,0 @@
use crate::ArchetypeAccess;
use bevy_hecs::{
Archetype, Component, ComponentError, Entity, Fetch, Query as HecsQuery, Ref, RefMut, With,
Without, World,
};
use bevy_tasks::ParallelIterator;
use std::{fmt, marker::PhantomData};
/// Provides scoped access to a World according to a given [HecsQuery]
#[derive(Debug)]
pub struct Query<'a, Q: HecsQuery> {
pub(crate) world: &'a World,
pub(crate) archetype_access: &'a ArchetypeAccess,
_marker: PhantomData<Q>,
}
/// An error that occurs when using a [Query]
#[derive(Debug)]
pub enum QueryError {
CannotReadArchetype,
CannotWriteArchetype,
ComponentError(ComponentError),
NoSuchEntity,
}
impl<'a, Q: HecsQuery> Query<'a, Q> {
#[inline]
pub fn new(world: &'a World, archetype_access: &'a ArchetypeAccess) -> Self {
Self {
world,
archetype_access,
_marker: PhantomData::default(),
}
}
#[inline]
pub fn iter(&mut self) -> QueryBorrowChecked<'_, Q> {
QueryBorrowChecked::new(&self.world.archetypes, self.archetype_access)
}
// TODO: find a way to make `iter`, `get`, `get_mut`, and `entity` safe without using tracking pointers with global locks
/// Gets a reference to the entity's component of the given type. This will fail if the entity does not have
/// the given component type or if the given component type does not match this query.
pub fn get<T: Component>(&self, entity: Entity) -> Result<Ref<T>, QueryError> {
if let Some(location) = self.world.get_entity_location(entity) {
if self
.archetype_access
.accessed
.contains(location.archetype as usize)
{
// SAFE: we have already checked that the entity/component matches our archetype access. and systems are scheduled to run with safe archetype access
unsafe {
self.world
.get_ref_at_location_unchecked(location)
.map_err(QueryError::ComponentError)
}
} else {
Err(QueryError::CannotReadArchetype)
}
} else {
Err(QueryError::ComponentError(ComponentError::NoSuchEntity))
}
}
pub fn entity(&mut self, entity: Entity) -> Result<QueryOneChecked<'_, Q>, QueryError> {
if let Some(location) = self.world.get_entity_location(entity) {
if self
.archetype_access
.accessed
.contains(location.archetype as usize)
{
// SAFE: we have already checked that the entity matches our archetype. and systems are scheduled to run with safe archetype access
Ok(unsafe {
QueryOneChecked::new(
&self.world.archetypes[location.archetype as usize],
location.index,
)
})
} else {
Err(QueryError::CannotReadArchetype)
}
} else {
Err(QueryError::NoSuchEntity)
}
}
/// Gets a mutable reference to the entity's component of the given type. This will fail if the entity does not have
/// the given component type or if the given component type does not match this query.
pub fn get_mut<T: Component>(&self, entity: Entity) -> Result<RefMut<'_, T>, QueryError> {
let location = match self.world.get_entity_location(entity) {
None => return Err(QueryError::ComponentError(ComponentError::NoSuchEntity)),
Some(location) => location,
};
if self
.archetype_access
.mutable
.contains(location.archetype as usize)
{
// SAFE: RefMut does exclusivity checks and we have already validated the entity
unsafe {
self.world
.get_ref_mut_at_location_unchecked(location)
.map_err(QueryError::ComponentError)
}
} else {
Err(QueryError::CannotWriteArchetype)
}
}
pub fn removed<C: Component>(&self) -> &[Entity] {
self.world.removed::<C>()
}
/// Sets the entity's component to the given value. This will fail if the entity does not already have
/// the given component type or if the given component type does not match this query.
pub fn set<T: Component>(&mut self, entity: Entity, component: T) -> Result<(), QueryError> {
let mut current = self.get_mut::<T>(entity)?;
*current = component;
Ok(())
}
}
/// A borrow of a `World` sufficient to execute the query `Q`
///
/// Note that borrows are not released until this object is dropped.
pub struct QueryBorrowChecked<'w, Q: HecsQuery> {
archetypes: &'w [Archetype],
archetype_access: &'w ArchetypeAccess,
borrowed: bool,
_marker: PhantomData<Q>,
}
impl<'w, Q: HecsQuery> fmt::Debug for QueryBorrowChecked<'w, Q> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("QueryBorrowChecked")
.field("archetypes", &self.archetypes)
.field("archetype_access", self.archetype_access)
.field("borrowed", &self.borrowed)
.field("_marker", &self._marker)
.finish()
}
}
impl<'w, Q: HecsQuery> QueryBorrowChecked<'w, Q> {
pub(crate) fn new(archetypes: &'w [Archetype], archetype_access: &'w ArchetypeAccess) -> Self {
Self {
archetypes,
borrowed: false,
archetype_access,
_marker: PhantomData,
}
}
/// Execute the query
///
/// Must be called only once per query.
#[inline]
pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, Q> {
self.borrow();
QueryIter {
borrow: self,
archetype_index: 0,
iter: ChunkIter::EMPTY,
}
}
/// Like `iter`, but returns child iterators of at most `batch_size`
/// elements
///
/// Useful for distributing work over a threadpool using the
/// ParallelIterator interface.
///
/// Batch size needs to be chosen based on the task being done in
/// parallel. The elements in each batch are computed serially, while
/// the batches themselves are computed in parallel.
///
/// A too small batch size can cause too much overhead, since scheduling
/// each batch could take longer than running the batch. On the other
/// hand, a too large batch size risks that one batch is still running
/// long after the rest have finished.
pub fn par_iter<'q>(&'q mut self, batch_size: usize) -> ParIter<'q, 'w, Q> {
self.borrow();
ParIter {
borrow: self,
archetype_index: 0,
batch_size,
batch: 0,
}
}
fn borrow(&mut self) {
if self.borrowed {
panic!(
"called QueryBorrowChecked::iter twice on the same borrow; construct a new query instead"
);
}
for index in self.archetype_access.accessed.ones() {
Q::Fetch::borrow(&self.archetypes[index]);
}
self.borrowed = true;
}
}
unsafe impl<'w, Q: HecsQuery> Send for QueryBorrowChecked<'w, Q> {}
unsafe impl<'w, Q: HecsQuery> Sync for QueryBorrowChecked<'w, Q> {}
impl<'w, Q: HecsQuery> Drop for QueryBorrowChecked<'w, Q> {
#[inline]
fn drop(&mut self) {
if self.borrowed {
for index in self.archetype_access.accessed.ones() {
Q::Fetch::release(&self.archetypes[index]);
}
}
}
}
impl<'q, 'w, Q: HecsQuery> IntoIterator for &'q mut QueryBorrowChecked<'w, Q> {
type IntoIter = QueryIter<'q, 'w, Q>;
type Item = <Q::Fetch as Fetch<'q>>::Item;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
/// Iterator over the set of entities with the components in `Q`
pub struct QueryIter<'q, 'w, Q: HecsQuery> {
borrow: &'q mut QueryBorrowChecked<'w, Q>,
archetype_index: usize,
iter: ChunkIter<Q>,
}
unsafe impl<'q, 'w, Q: HecsQuery> Send for QueryIter<'q, 'w, Q> {}
unsafe impl<'q, 'w, Q: HecsQuery> Sync for QueryIter<'q, 'w, Q> {}
impl<'q, 'w, Q: HecsQuery> Iterator for QueryIter<'q, 'w, Q> {
type Item = <Q::Fetch as Fetch<'q>>::Item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
loop {
match unsafe { self.iter.next() } {
None => {
let archetype = self.borrow.archetypes.get(self.archetype_index)?;
self.archetype_index += 1;
unsafe {
self.iter = Q::Fetch::get(archetype, 0).map_or(ChunkIter::EMPTY, |fetch| {
ChunkIter {
fetch,
len: archetype.len(),
position: 0,
}
});
}
}
Some(components) => return Some(components),
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let n = self.len();
(n, Some(n))
}
}
impl<'q, 'w, Q: HecsQuery> ExactSizeIterator for QueryIter<'q, 'w, Q> {
fn len(&self) -> usize {
self.borrow
.archetypes
.iter()
.filter(|&x| Q::Fetch::access(x).is_some())
.map(|x| x.len())
.sum()
}
}
struct ChunkIter<Q: HecsQuery> {
fetch: Q::Fetch,
position: usize,
len: usize,
}
impl<Q: HecsQuery> ChunkIter<Q> {
#[allow(clippy::declare_interior_mutable_const)] // no trait bounds on const fns
const EMPTY: Self = Self {
fetch: Q::Fetch::DANGLING,
position: 0,
len: 0,
};
unsafe fn next<'a>(&mut self) -> Option<<Q::Fetch as Fetch<'a>>::Item> {
loop {
if self.position == self.len {
return None;
}
if self.fetch.should_skip(self.position as usize) {
self.position += 1;
continue;
}
let item = Some(self.fetch.fetch(self.position as usize));
self.position += 1;
return item;
}
}
}
/// Batched version of `QueryIter`
pub struct ParIter<'q, 'w, Q: HecsQuery> {
borrow: &'q mut QueryBorrowChecked<'w, Q>,
archetype_index: usize,
batch_size: usize,
batch: usize,
}
impl<'q, 'w, Q: HecsQuery> ParallelIterator<Batch<'q, Q>> for ParIter<'q, 'w, Q> {
type Item = <Q::Fetch as Fetch<'q>>::Item;
fn next_batch(&mut self) -> Option<Batch<'q, Q>> {
loop {
let archetype = self.borrow.archetypes.get(self.archetype_index)?;
let offset = self.batch_size * self.batch;
if offset >= archetype.len() {
self.archetype_index += 1;
self.batch = 0;
continue;
}
if let Some(fetch) = unsafe { Q::Fetch::get(archetype, offset as usize) } {
self.batch += 1;
return Some(Batch {
_marker: PhantomData,
state: ChunkIter {
fetch,
len: self.batch_size.min(archetype.len() - offset),
position: 0,
},
});
} else {
self.archetype_index += 1;
debug_assert_eq!(
self.batch, 0,
"query fetch should always reject at the first batch or not at all"
);
continue;
}
}
}
}
/// A sequence of entities yielded by `ParIter`
pub struct Batch<'q, Q: HecsQuery> {
_marker: PhantomData<&'q ()>,
state: ChunkIter<Q>,
}
impl<'q, 'w, Q: HecsQuery> Iterator for Batch<'q, Q> {
type Item = <Q::Fetch as Fetch<'q>>::Item;
fn next(&mut self) -> Option<Self::Item> {
let components = unsafe { self.state.next()? };
Some(components)
}
}
unsafe impl<'q, Q: HecsQuery> Send for Batch<'q, Q> {}
/// A borrow of a `World` sufficient to execute the query `Q` on a single entity
#[derive(Debug)]
pub struct QueryOneChecked<'a, Q: HecsQuery> {
archetype: &'a Archetype,
index: usize,
borrowed: bool,
_marker: PhantomData<Q>,
}
impl<'a, Q: HecsQuery> QueryOneChecked<'a, Q> {
/// Construct a query accessing the entity in `archetype` at `index`
///
/// # Safety
///
/// `index` must be in-bounds for `archetype`
pub(crate) unsafe fn new(archetype: &'a Archetype, index: usize) -> Self {
Self {
archetype,
index,
borrowed: false,
_marker: PhantomData,
}
}
/// Get the query result, or `None` if the entity does not satisfy the query
///
/// Must be called at most once.
///
/// Panics if called more than once or if it would construct a borrow that clashes with another
/// pre-existing borrow.
pub fn get(&mut self) -> Option<<Q::Fetch as Fetch<'_>>::Item> {
unsafe {
let fetch = Q::Fetch::get(self.archetype, self.index as usize)?;
self.borrowed = true;
if fetch.should_skip(0) {
None
} else {
Q::Fetch::borrow(self.archetype);
Some(fetch.fetch(0))
}
}
}
/// Transform the query into one that requires a certain component without borrowing it
///
/// See `QueryBorrow::with` for details.
pub fn with<T: Component>(self) -> QueryOneChecked<'a, With<T, Q>> {
self.transform()
}
/// Transform the query into one that skips entities having a certain component
///
/// See `QueryBorrow::without` for details.
pub fn without<T: Component>(self) -> QueryOneChecked<'a, Without<T, Q>> {
self.transform()
}
/// Helper to change the type of the query
fn transform<R: HecsQuery>(self) -> QueryOneChecked<'a, R> {
QueryOneChecked {
archetype: self.archetype,
index: self.index,
borrowed: self.borrowed,
_marker: PhantomData,
}
}
}
impl<Q: HecsQuery> Drop for QueryOneChecked<'_, Q> {
fn drop(&mut self) {
if self.borrowed {
Q::Fetch::release(self.archetype);
}
}
}
unsafe impl<Q: HecsQuery> Send for QueryOneChecked<'_, Q> {}
unsafe impl<Q: HecsQuery> Sync for QueryOneChecked<'_, Q> {}

View File

@ -0,0 +1,203 @@
mod query_set;
pub use query_set::*;
use bevy_hecs::{
ArchetypeComponent, Batch, BatchedIter, Component, ComponentError, Entity, Fetch, Mut,
Query as HecsQuery, QueryIter, ReadOnlyFetch, TypeAccess, World,
};
use bevy_tasks::ParallelIterator;
use std::marker::PhantomData;
/// Provides scoped access to a World according to a given [HecsQuery]
#[derive(Debug)]
pub struct Query<'a, Q: HecsQuery> {
pub(crate) world: &'a World,
pub(crate) component_access: &'a TypeAccess<ArchetypeComponent>,
_marker: PhantomData<Q>,
}
/// An error that occurs when using a [Query]
#[derive(Debug)]
pub enum QueryError {
CannotReadArchetype,
CannotWriteArchetype,
ComponentError(ComponentError),
NoSuchEntity,
}
impl<'a, Q: HecsQuery> Query<'a, Q> {
#[inline]
pub fn new(world: &'a World, component_access: &'a TypeAccess<ArchetypeComponent>) -> Self {
Self {
world,
component_access,
_marker: PhantomData::default(),
}
}
/// Iterates over the query results. This can only be called for read-only queries
pub fn iter(&self) -> QueryIter<'_, Q>
where
Q::Fetch: ReadOnlyFetch,
{
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe { self.world.query_unchecked() }
}
/// Iterates over the query results
pub fn iter_mut(&mut self) -> QueryIter<'_, Q> {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe { self.world.query_unchecked() }
}
/// Iterates over the query results
/// # Safety
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component
pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, Q> {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
self.world.query_unchecked()
}
#[inline]
pub fn par_iter(&self, batch_size: usize) -> ParIter<'_, Q>
where
Q::Fetch: ReadOnlyFetch,
{
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe { ParIter::new(self.world.query_batched_unchecked(batch_size)) }
}
#[inline]
pub fn par_iter_mut(&mut self, batch_size: usize) -> ParIter<'_, Q> {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe { ParIter::new(self.world.query_batched_unchecked(batch_size)) }
}
/// Gets the query result for the given `entity`
pub fn entity(&self, entity: Entity) -> Result<<Q::Fetch as Fetch>::Item, QueryError>
where
Q::Fetch: ReadOnlyFetch,
{
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe {
self.world
.query_one_unchecked::<Q>(entity)
.map_err(|_err| QueryError::NoSuchEntity)
}
}
/// Gets the query result for the given `entity`
pub fn entity_mut(&mut self, entity: Entity) -> Result<<Q::Fetch as Fetch>::Item, QueryError> {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe {
self.world
.query_one_unchecked::<Q>(entity)
.map_err(|_err| QueryError::NoSuchEntity)
}
}
/// Gets the query result for the given `entity`
/// # Safety
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component
pub unsafe fn entity_unsafe(
&self,
entity: Entity,
) -> Result<<Q::Fetch as Fetch>::Item, QueryError> {
self.world
.query_one_unchecked::<Q>(entity)
.map_err(|_err| QueryError::NoSuchEntity)
}
/// Gets a reference to the entity's component of the given type. This will fail if the entity does not have
/// the given component type or if the given component type does not match this query.
pub fn get<T: Component>(&self, entity: Entity) -> Result<&T, QueryError> {
if let Some(location) = self.world.get_entity_location(entity) {
if self
.component_access
.is_read_or_write(&ArchetypeComponent::new::<T>(location.archetype))
{
// SAFE: we have already checked that the entity/component matches our archetype access. and systems are scheduled to run with safe archetype access
unsafe {
self.world
.get_at_location_unchecked(location)
.map_err(QueryError::ComponentError)
}
} else {
Err(QueryError::CannotReadArchetype)
}
} else {
Err(QueryError::ComponentError(ComponentError::NoSuchEntity))
}
}
/// Gets a mutable reference to the entity's component of the given type. This will fail if the entity does not have
/// the given component type or if the given component type does not match this query.
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Result<Mut<'_, T>, QueryError> {
let location = match self.world.get_entity_location(entity) {
None => return Err(QueryError::ComponentError(ComponentError::NoSuchEntity)),
Some(location) => location,
};
if self
.component_access
.is_write(&ArchetypeComponent::new::<T>(location.archetype))
{
// SAFE: RefMut does exclusivity checks and we have already validated the entity
unsafe {
self.world
.get_mut_at_location_unchecked(location)
.map_err(QueryError::ComponentError)
}
} else {
Err(QueryError::CannotWriteArchetype)
}
}
/// Gets a mutable reference to the entity's component of the given type. This will fail if the entity does not have
/// the given component type
/// # Safety
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component
pub unsafe fn get_unsafe<T: Component>(
&self,
entity: Entity,
) -> Result<Mut<'_, T>, QueryError> {
self.world
.get_mut_unchecked(entity)
.map_err(QueryError::ComponentError)
}
pub fn removed<C: Component>(&self) -> &[Entity] {
self.world.removed::<C>()
}
/// Sets the entity's component to the given value. This will fail if the entity does not already have
/// the given component type or if the given component type does not match this query.
pub fn set<T: Component>(&mut self, entity: Entity, component: T) -> Result<(), QueryError> {
let mut current = self.get_mut::<T>(entity)?;
*current = component;
Ok(())
}
}
/// Parallel version of QueryIter
pub struct ParIter<'w, Q: HecsQuery> {
batched_iter: BatchedIter<'w, Q>,
}
impl<'w, Q: HecsQuery> ParIter<'w, Q> {
pub fn new(batched_iter: BatchedIter<'w, Q>) -> Self {
Self { batched_iter }
}
}
unsafe impl<'w, Q: HecsQuery> Send for ParIter<'w, Q> {}
impl<'w, Q: HecsQuery> ParallelIterator<Batch<'w, Q>> for ParIter<'w, Q> {
type Item = <Q::Fetch as Fetch<'w>>::Item;
#[inline]
fn next_batch(&mut self) -> Option<Batch<'w, Q>> {
self.batched_iter.next()
}
}

View File

@ -0,0 +1,25 @@
use crate::Query;
use bevy_hecs::{
impl_query_set, ArchetypeComponent, Fetch, Query as HecsQuery, QueryAccess, TypeAccess, World,
};
pub struct QuerySet<T: QueryTuple> {
value: T,
}
impl_query_set!();
pub trait QueryTuple {
/// # Safety
/// this might cast world and component access to the relevant Self lifetimes. verify that this is safe in each impl
unsafe fn new(world: &World, component_access: &TypeAccess<ArchetypeComponent>) -> Self;
fn get_accesses() -> Vec<QueryAccess>;
}
impl<T: QueryTuple> QuerySet<T> {
pub fn new(world: &World, component_access: &TypeAccess<ArchetypeComponent>) -> Self {
QuerySet {
value: unsafe { T::new(world, component_access) },
}
}
}

View File

@ -1,7 +1,5 @@
use crate::resource::Resources;
use bevy_hecs::{Access, Query, World};
use bevy_utils::HashSet;
use fixedbitset::FixedBitSet;
use bevy_hecs::{ArchetypeComponent, TypeAccess, World};
use std::{any::TypeId, borrow::Cow};
/// Determines the strategy used to run the `run_thread_local` function in a [System]
@ -25,130 +23,11 @@ impl SystemId {
pub trait System: Send + Sync {
fn name(&self) -> Cow<'static, str>;
fn id(&self) -> SystemId;
fn update_archetype_access(&mut self, world: &World);
fn archetype_access(&self) -> &ArchetypeAccess;
fn resource_access(&self) -> &TypeAccess;
fn update(&mut self, world: &World);
fn archetype_component_access(&self) -> &TypeAccess<ArchetypeComponent>;
fn resource_access(&self) -> &TypeAccess<TypeId>;
fn thread_local_execution(&self) -> ThreadLocalExecution;
fn run(&mut self, world: &World, resources: &Resources);
fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources);
fn initialize(&mut self, _world: &mut World, _resources: &mut Resources) {}
}
/// Provides information about the archetypes a [System] reads and writes
#[derive(Debug, Default)]
pub struct ArchetypeAccess {
pub accessed: FixedBitSet, // union of both immutable and mutable
pub mutable: FixedBitSet,
}
// credit to Ratysz from the Yaks codebase
impl ArchetypeAccess {
pub fn is_compatible(&self, other: &ArchetypeAccess) -> bool {
self.mutable.is_disjoint(&other.accessed) && self.accessed.is_disjoint(&other.mutable)
}
pub fn union(&mut self, other: &ArchetypeAccess) {
self.mutable.union_with(&other.mutable);
self.accessed.union_with(&other.accessed);
}
pub fn set_access_for_query<Q>(&mut self, world: &World)
where
Q: Query,
{
let iterator = world.archetypes();
let bits = iterator.len();
self.accessed.grow(bits);
self.mutable.grow(bits);
iterator
.enumerate()
.filter_map(|(index, archetype)| archetype.access::<Q>().map(|access| (index, access)))
.for_each(|(archetype, access)| match access {
Access::Read => self.accessed.set(archetype, true),
Access::Write => {
self.accessed.set(archetype, true);
self.mutable.set(archetype, true);
}
Access::Iterate => (),
});
}
pub fn clear(&mut self) {
self.accessed.clear();
self.mutable.clear();
}
}
/// Provides information about the types a [System] reads and writes
#[derive(Debug, Default, Eq, PartialEq, Clone)]
pub struct TypeAccess {
pub immutable: HashSet<TypeId>,
pub mutable: HashSet<TypeId>,
}
impl TypeAccess {
pub fn is_compatible(&self, other: &TypeAccess) -> bool {
self.mutable.is_disjoint(&other.mutable)
&& self.mutable.is_disjoint(&other.immutable)
&& self.immutable.is_disjoint(&other.mutable)
}
pub fn union(&mut self, other: &TypeAccess) {
self.mutable.extend(&other.mutable);
self.immutable.extend(&other.immutable);
}
pub fn clear(&mut self) {
self.immutable.clear();
self.mutable.clear();
}
}
#[cfg(test)]
mod tests {
use super::{ArchetypeAccess, TypeAccess};
use crate::resource::{FetchResource, Res, ResMut, ResourceQuery};
use bevy_hecs::World;
use std::any::TypeId;
struct A;
struct B;
struct C;
#[test]
fn query_archetype_access() {
let mut world = World::default();
let e1 = world.spawn((A,));
let e2 = world.spawn((A, B));
let e3 = world.spawn((A, B, C));
let mut access = ArchetypeAccess::default();
access.set_access_for_query::<(&A,)>(&world);
let e1_archetype = world.get_entity_location(e1).unwrap().archetype as usize;
let e2_archetype = world.get_entity_location(e2).unwrap().archetype as usize;
let e3_archetype = world.get_entity_location(e3).unwrap().archetype as usize;
assert!(access.accessed.contains(e1_archetype));
assert!(access.accessed.contains(e2_archetype));
assert!(access.accessed.contains(e3_archetype));
let mut access = ArchetypeAccess::default();
access.set_access_for_query::<(&A, &B)>(&world);
assert!(access.accessed.contains(e1_archetype) == false);
assert!(access.accessed.contains(e2_archetype));
assert!(access.accessed.contains(e3_archetype));
}
#[test]
fn resource_query_access() {
let access =
<<(Res<A>, ResMut<B>, Res<C>) as ResourceQuery>::Fetch as FetchResource>::access();
let mut expected_access = TypeAccess::default();
expected_access.immutable.insert(TypeId::of::<A>());
expected_access.immutable.insert(TypeId::of::<C>());
expected_access.mutable.insert(TypeId::of::<B>());
assert_eq!(access, expected_access);
}
}

View File

@ -80,12 +80,12 @@ pub fn lights_node_system(
render_resource_context: Res<Box<dyn RenderResourceContext>>,
// TODO: this write on RenderResourceBindings will prevent this system from running in parallel with other systems that do the same
mut render_resource_bindings: ResMut<RenderResourceBindings>,
mut query: Query<(&Light, &GlobalTransform)>,
query: Query<(&Light, &GlobalTransform)>,
) {
let state = &mut state;
let render_resource_context = &**render_resource_context;
let light_count = query.iter().iter().count();
let light_count = query.iter().count();
let size = std::mem::size_of::<LightRaw>();
let light_count_size = std::mem::size_of::<LightCount>();
let light_array_size = size * light_count;
@ -133,7 +133,6 @@ pub fn lights_node_system(
// light array
for ((light, global_transform), slot) in query
.iter()
.iter()
.zip(data[light_count_size..current_light_uniform_size].chunks_exact_mut(size))
{

View File

@ -23,11 +23,11 @@ impl ActiveCameras {
pub fn active_cameras_system(
mut active_cameras: ResMut<ActiveCameras>,
mut query: Query<(Entity, &Camera)>,
query: Query<(Entity, &Camera)>,
) {
for (name, active_camera) in active_cameras.cameras.iter_mut() {
if active_camera.is_none() {
for (camera_entity, camera) in &mut query.iter() {
for (camera_entity, camera) in query.iter() {
if let Some(ref current_name) = camera.name {
if current_name == name {
*active_camera = Some(camera_entity);

View File

@ -1,6 +1,6 @@
use super::CameraProjection;
use bevy_app::prelude::{EventReader, Events};
use bevy_ecs::{Added, Component, Entity, Local, Query, Res};
use bevy_ecs::{Added, Component, Entity, Local, Query, QuerySet, Res};
use bevy_math::Mat4;
use bevy_property::Properties;
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
@ -38,8 +38,10 @@ pub fn camera_system<T: CameraProjection + Component>(
window_resized_events: Res<Events<WindowResized>>,
window_created_events: Res<Events<WindowCreated>>,
windows: Res<Windows>,
mut query: Query<(Entity, &mut Camera, &mut T)>,
mut query_added: Query<(Entity, Added<Camera>)>,
mut queries: QuerySet<(
Query<(Entity, &mut Camera, &mut T)>,
Query<(Entity, Added<Camera>)>,
)>,
) {
let mut changed_window_ids = Vec::new();
// handle resize events. latest events are handled first because we only want to resize each window once
@ -69,10 +71,10 @@ pub fn camera_system<T: CameraProjection + Component>(
}
let mut added_cameras = vec![];
for (entity, _camera) in &mut query_added.iter() {
for (entity, _camera) in &mut queries.q1().iter() {
added_cameras.push(entity);
}
for (entity, mut camera, mut camera_projection) in &mut query.iter() {
for (entity, mut camera, mut camera_projection) in queries.q0_mut().iter_mut() {
if let Some(window) = windows.get(camera.window) {
if changed_window_ids.contains(&window.id()) || added_cameras.contains(&entity) {
camera_projection.update(window.width() as usize, window.height() as usize);

View File

@ -25,16 +25,16 @@ impl VisibleEntities {
pub fn visible_entities_system(
mut camera_query: Query<(&Camera, &GlobalTransform, &mut VisibleEntities)>,
mut draw_query: Query<(Entity, &Draw)>,
draw_query: Query<(Entity, &Draw)>,
draw_transform_query: Query<(&Draw, &GlobalTransform)>,
) {
for (camera, camera_global_transform, mut visible_entities) in &mut camera_query.iter() {
for (camera, camera_global_transform, mut visible_entities) in camera_query.iter_mut() {
visible_entities.value.clear();
let camera_position = camera_global_transform.translation;
let mut no_transform_order = 0.0;
let mut transparent_entities = Vec::new();
for (entity, draw) in &mut draw_query.iter() {
for (entity, draw) in draw_query.iter() {
if !draw.is_visible {
continue;
}

View File

@ -215,20 +215,14 @@ impl<'a> FetchResource<'a> for FetchDrawContext {
}
}
fn access() -> TypeAccess {
fn access() -> TypeAccess<TypeId> {
let mut access = TypeAccess::default();
access
.mutable
.insert(TypeId::of::<Assets<PipelineDescriptor>>());
access.mutable.insert(TypeId::of::<Assets<Shader>>());
access.mutable.insert(TypeId::of::<PipelineCompiler>());
access
.immutable
.insert(TypeId::of::<Box<dyn RenderResourceContext>>());
access
.immutable
.insert(TypeId::of::<VertexBufferDescriptors>());
access.immutable.insert(TypeId::of::<SharedBuffers>());
access.add_write(TypeId::of::<Assets<PipelineDescriptor>>());
access.add_write(TypeId::of::<Assets<Shader>>());
access.add_write(TypeId::of::<PipelineCompiler>());
access.add_read(TypeId::of::<Box<dyn RenderResourceContext>>());
access.add_read(TypeId::of::<VertexBufferDescriptors>());
access.add_read(TypeId::of::<SharedBuffers>());
access
}
}
@ -388,7 +382,7 @@ pub trait Drawable {
}
pub fn clear_draw_system(mut query: Query<&mut Draw>) {
for mut draw in &mut query.iter() {
for mut draw in query.iter_mut() {
draw.clear_render_commands();
}
}

View File

@ -575,7 +575,7 @@ pub fn mesh_resource_provider_system(
}
// TODO: remove this once batches are pipeline specific and deprecate assigned_meshes draw target
for (handle, mut render_pipelines) in &mut query.iter() {
for (handle, mut render_pipelines) in query.iter_mut() {
if let Some(mesh) = meshes.get(handle) {
for render_pipeline in render_pipelines.pipelines.iter_mut() {
render_pipeline.specialization.primitive_topology = mesh.primitive_topology;

View File

@ -79,7 +79,7 @@ pub fn draw_render_pipelines_system(
meshes: Res<Assets<Mesh>>,
mut query: Query<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>)>,
) {
for (mut draw, mut render_pipelines, mesh_handle) in &mut query.iter() {
for (mut draw, mut render_pipelines, mesh_handle) in query.iter_mut() {
if !draw.is_visible {
continue;
}

View File

@ -224,11 +224,9 @@ where
// attempt to draw each visible entity
let mut draw_state = DrawState::default();
for visible_entity in visible_entities.iter() {
if let Ok(query_one) = world.query_one::<Q>(visible_entity.entity) {
if query_one.get().is_none() {
// visible entity does not match the Pass query
continue;
}
if world.query_one::<Q>(visible_entity.entity).is_err() {
// visible entity does not match the Pass query
continue;
}
let draw = if let Ok(draw) = world.get::<Draw>(visible_entity.entity) {

View File

@ -433,7 +433,7 @@ fn render_resources_node_system<T: RenderResources>(
let render_resource_context = &**render_resource_context;
uniform_buffer_arrays.begin_update();
// initialize uniform buffer arrays using the first RenderResources
if let Some((_, first, _, _)) = query.iter().iter().next() {
if let Some((_, first, _, _)) = query.iter_mut().next() {
uniform_buffer_arrays.initialize(first);
}
@ -441,7 +441,7 @@ fn render_resources_node_system<T: RenderResources>(
uniform_buffer_arrays.remove_bindings(*entity);
}
for (entity, uniforms, draw, mut render_pipelines) in &mut query.iter() {
for (entity, uniforms, draw, mut render_pipelines) in query.iter_mut() {
if !draw.is_visible {
continue;
}
@ -463,7 +463,7 @@ fn render_resources_node_system<T: RenderResources>(
staging_buffer,
0..state.uniform_buffer_arrays.staging_buffer_size as u64,
&mut |mut staging_buffer, _render_resource_context| {
for (entity, uniforms, draw, mut render_pipelines) in &mut query.iter() {
for (entity, uniforms, draw, mut render_pipelines) in query.iter_mut() {
if !draw.is_visible {
continue;
}
@ -487,7 +487,7 @@ fn render_resources_node_system<T: RenderResources>(
} else {
// TODO: can we just remove this?
let mut staging_buffer: [u8; 0] = [];
for (entity, uniforms, draw, mut render_pipelines) in &mut query.iter() {
for (entity, uniforms, draw, mut render_pipelines) in query.iter_mut() {
if !draw.is_visible {
continue;
}
@ -640,7 +640,7 @@ fn asset_render_resources_node_system<T: RenderResources + Asset>(
}
}
for (asset_handle, draw, mut render_pipelines) in &mut query.iter() {
for (asset_handle, draw, mut render_pipelines) in query.iter_mut() {
if !draw.is_visible {
continue;
}

View File

@ -64,7 +64,7 @@ pub fn shader_defs_system<T>(mut query: Query<(&T, &mut RenderPipelines)>)
where
T: ShaderDefs + Send + Sync + 'static,
{
for (shader_defs, mut render_pipelines) in &mut query.iter() {
for (shader_defs, mut render_pipelines) in query.iter_mut() {
for shader_def in shader_defs.iter_shader_defs() {
for render_pipeline in render_pipelines.pipelines.iter_mut() {
render_pipeline
@ -79,7 +79,7 @@ where
/// Clears each [RenderPipelines]' shader defs collection
pub fn clear_shader_defs_system(mut query: Query<&mut RenderPipelines>) {
for mut render_pipelines in &mut query.iter() {
for mut render_pipelines in query.iter_mut() {
for render_pipeline in render_pipelines.pipelines.iter_mut() {
render_pipeline
.specialization
@ -97,7 +97,7 @@ pub fn asset_shader_defs_system<T: Asset>(
) where
T: ShaderDefs + Send + Sync + 'static,
{
for (asset_handle, mut render_pipelines) in &mut query.iter() {
for (asset_handle, mut render_pipelines) in query.iter_mut() {
let shader_defs = assets.get(asset_handle).unwrap();
for shader_def in shader_defs.iter_shader_defs() {
for render_pipeline in render_pipelines.pipelines.iter_mut() {

View File

@ -41,7 +41,7 @@ pub fn sprite_system(
textures: Res<Assets<Texture>>,
mut query: Query<(&mut Sprite, &Handle<ColorMaterial>)>,
) {
for (mut sprite, handle) in &mut query.iter() {
for (mut sprite, handle) in query.iter_mut() {
match sprite.resize_mode {
SpriteResizeMode::Manual => continue,
SpriteResizeMode::Automatic => {

View File

@ -12,17 +12,10 @@ pub fn run_on_hierarchy<T, S>(
where
T: Clone,
{
// TODO: not a huge fan of this pattern. are there ways to do recursive updates in legion without allocations?
// TODO: the problem above might be resolvable with world splitting
let children = children_query
.get::<Children>(entity)
.ok()
.map(|children| children.0.iter().cloned().collect::<Vec<Entity>>());
let parent_result = run(state, entity, parent_result, previous_result);
previous_result = None;
if let Some(children) = children {
for child in children {
if let Ok(children) = children_query.entity(entity) {
for child in children.iter().cloned() {
previous_result = run_on_hierarchy(
children_query,
state,
@ -51,6 +44,7 @@ fn despawn_with_children_recursive(world: &mut World, entity: Entity) {
children.retain(|c| *c != entity);
}
}
// then despawn the entity and all of its children
despawn_with_children_recursive_inner(world, entity);
}
@ -135,7 +129,6 @@ mod tests {
let results = world
.query::<(&u32, &u64)>()
.iter()
.map(|(a, b)| (*a, *b))
.collect::<Vec<_>>();

View File

@ -5,18 +5,16 @@ use smallvec::SmallVec;
pub fn parent_update_system(
mut commands: Commands,
mut removed_parent_query: Query<Without<Parent, (Entity, &PreviousParent)>>,
removed_parent_query: Query<Without<Parent, (Entity, &PreviousParent)>>,
// TODO: ideally this only runs when the Parent component has changed
mut changed_parent_query: Query<(Entity, &Parent, Option<&mut PreviousParent>)>,
children_query: Query<&mut Children>,
mut children_query: Query<&mut Children>,
) {
// Entities with a missing `Parent` (ie. ones that have a `PreviousParent`), remove
// them from the `Children` of the `PreviousParent`.
for (entity, previous_parent) in &mut removed_parent_query.iter() {
for (entity, previous_parent) in removed_parent_query.iter() {
log::trace!("Parent was removed from {:?}", entity);
if let Ok(mut previous_parent_children) =
children_query.get_mut::<Children>(previous_parent.0)
{
if let Ok(mut previous_parent_children) = children_query.entity_mut(previous_parent.0) {
log::trace!(" > Removing {:?} from it's prev parent's children", entity);
previous_parent_children.0.retain(|e| *e != entity);
commands.remove_one::<PreviousParent>(entity);
@ -27,7 +25,7 @@ pub fn parent_update_system(
let mut children_additions = HashMap::<Entity, SmallVec<[Entity; 8]>>::default();
// Entities with a changed Parent (that also have a PreviousParent, even if None)
for (entity, parent, possible_previous_parent) in &mut changed_parent_query.iter() {
for (entity, parent, possible_previous_parent) in changed_parent_query.iter_mut() {
log::trace!("Parent changed for {:?}", entity);
if let Some(mut previous_parent) = possible_previous_parent {
// New and previous point to the same Entity, carry on, nothing to see here.

View File

@ -2,10 +2,15 @@ use crate::components::*;
use bevy_ecs::prelude::*;
pub fn transform_propagate_system(
mut root_query: Query<Without<Parent, (Option<&Children>, &Transform, &mut GlobalTransform)>>,
mut transform_query: Query<(&Transform, &mut GlobalTransform, Option<&Children>)>,
mut root_query: Query<
Without<
Parent,
With<GlobalTransform, (Option<&Children>, &Transform, &mut GlobalTransform)>,
>,
>,
mut transform_query: Query<With<Parent, (&Transform, &mut GlobalTransform, Option<&Children>)>>,
) {
for (children, transform, mut global_transform) in &mut root_query.iter() {
for (children, transform, mut global_transform) in root_query.iter_mut() {
*global_transform = GlobalTransform::from(*transform);
if let Some(children) = children {
@ -18,16 +23,15 @@ pub fn transform_propagate_system(
fn propagate_recursive(
parent: &GlobalTransform,
transform_query: &mut Query<(&Transform, &mut GlobalTransform, Option<&Children>)>,
transform_query: &mut Query<
With<Parent, (&Transform, &mut GlobalTransform, Option<&Children>)>,
>,
entity: Entity,
) {
log::trace!("Updating Transform for {:?}", entity);
let global_matrix = {
if let (Ok(transform), Ok(mut global_transform)) = (
transform_query.get::<Transform>(entity),
transform_query.get_mut::<GlobalTransform>(entity),
) {
if let Ok((transform, mut global_transform, _)) = transform_query.entity_mut(entity) {
*global_transform = parent.mul_transform(*transform);
*global_transform
} else {

View File

@ -160,10 +160,10 @@ unsafe impl Sync for FlexSurface {}
pub fn flex_node_system(
windows: Res<Windows>,
mut flex_surface: ResMut<FlexSurface>,
mut root_node_query: Query<With<Node, Without<Parent, Entity>>>,
mut node_query: Query<With<Node, (Entity, Changed<Style>, Option<&CalculatedSize>)>>,
mut changed_size_query: Query<With<Node, (Entity, &Style, Changed<CalculatedSize>)>>,
mut children_query: Query<With<Node, (Entity, Changed<Children>)>>,
root_node_query: Query<With<Node, Without<Parent, Entity>>>,
node_query: Query<With<Node, (Entity, Changed<Style>, Option<&CalculatedSize>)>>,
changed_size_query: Query<With<Node, (Entity, &Style, Changed<CalculatedSize>)>>,
children_query: Query<With<Node, (Entity, Changed<Children>)>>,
mut node_transform_query: Query<(Entity, &mut Node, &mut Transform, Option<&Parent>)>,
) {
// update window root nodes
@ -172,7 +172,7 @@ pub fn flex_node_system(
}
// update changed nodes
for (entity, style, calculated_size) in &mut node_query.iter() {
for (entity, style, calculated_size) in node_query.iter() {
// TODO: remove node from old hierarchy if its root has changed
if let Some(calculated_size) = calculated_size {
flex_surface.upsert_leaf(entity, &style, *calculated_size);
@ -181,7 +181,7 @@ pub fn flex_node_system(
}
}
for (entity, style, calculated_size) in &mut changed_size_query.iter() {
for (entity, style, calculated_size) in changed_size_query.iter() {
flex_surface.upsert_leaf(entity, &style, *calculated_size);
}
@ -189,18 +189,18 @@ pub fn flex_node_system(
// update window children (for now assuming all Nodes live in the primary window)
if let Some(primary_window) = windows.get_primary() {
flex_surface.set_window_children(primary_window.id(), root_node_query.iter().iter());
flex_surface.set_window_children(primary_window.id(), root_node_query.iter());
}
// update children
for (entity, children) in &mut children_query.iter() {
for (entity, children) in children_query.iter() {
flex_surface.update_children(entity, &children);
}
// compute layouts
flex_surface.compute_window_layouts();
for (entity, mut node, mut transform, parent) in &mut node_transform_query.iter() {
for (entity, mut node, mut transform, parent) in node_transform_query.iter_mut() {
let layout = flex_surface.get_layout(entity).unwrap();
node.size = Vec2::new(layout.size.width, layout.size.height);
let position = &mut transform.translation;

View File

@ -56,8 +56,7 @@ pub fn ui_focus_system(
}
if mouse_button_input.just_released(MouseButton::Left) {
for (_entity, _node, _global_transform, interaction, _focus_policy) in
&mut node_query.iter()
for (_entity, _node, _global_transform, interaction, _focus_policy) in node_query.iter_mut()
{
if let Some(mut interaction) = interaction {
if *interaction == Interaction::Clicked {
@ -71,9 +70,8 @@ pub fn ui_focus_system(
let mut hovered_entity = None;
{
let mut query_iter = node_query.iter();
let mut moused_over_z_sorted_nodes = query_iter
.iter()
let mut moused_over_z_sorted_nodes = node_query
.iter_mut()
.filter_map(
|(entity, node, global_transform, interaction, focus_policy)| {
let position = global_transform.translation;

View File

@ -8,18 +8,13 @@ use bevy_transform::{
pub const UI_Z_STEP: f32 = 0.001;
pub fn ui_z_system(
mut root_node_query: Query<With<Node, Without<Parent, Entity>>>,
root_node_query: Query<With<Node, Without<Parent, Entity>>>,
mut node_query: Query<(Entity, &Node, &mut Transform)>,
children_query: Query<&Children>,
) {
let mut current_global_z = 0.0;
// PERF: we can probably avoid an allocation here by making root_node_query and node_query non-overlapping
let root_nodes = (&mut root_node_query.iter())
.iter()
.collect::<Vec<Entity>>();
for entity in root_nodes {
for entity in root_node_query.iter() {
if let Some(result) = hierarchy::run_on_hierarchy(
&children_query,
&mut node_query,
@ -46,8 +41,9 @@ fn update_node_entity(
};
let global_z = z + parent_global_z;
let mut transform = node_query.get_mut::<Transform>(entity).ok()?;
transform.translation.set_z(z);
if let Ok(mut transform) = node_query.get_mut::<Transform>(entity) {
transform.translation.set_z(z);
}
Some(global_z)
}

View File

@ -21,7 +21,7 @@ pub fn image_node_system(
textures: Res<Assets<Texture>>,
mut query: Query<(&Image, &mut CalculatedSize, &Handle<ColorMaterial>)>,
) {
for (_image, mut calculated_size, material_handle) in &mut query.iter() {
for (_image, mut calculated_size, material_handle) in query.iter_mut() {
if let Some(texture) = materials
.get(material_handle)
.and_then(|material| material.texture.as_ref())

View File

@ -1,6 +1,6 @@
use crate::{CalculatedSize, Node};
use bevy_asset::{Assets, Handle};
use bevy_ecs::{Changed, Entity, Local, Query, Res, ResMut};
use bevy_ecs::{Changed, Entity, Local, Query, QuerySet, Res, ResMut};
use bevy_math::Size;
use bevy_render::{
draw::{Draw, DrawContext, Drawable},
@ -30,33 +30,33 @@ pub fn text_system(
fonts: Res<Assets<Font>>,
mut font_atlas_sets: ResMut<Assets<FontAtlasSet>>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
mut query: Query<(Entity, Changed<Text>, &mut CalculatedSize)>,
mut text_query: Query<(&Text, &mut CalculatedSize)>,
mut queries: QuerySet<(
Query<(Entity, Changed<Text>, &mut CalculatedSize)>,
Query<(&Text, &mut CalculatedSize)>,
)>,
) {
// add queued text to atlases
let mut new_queued_text = Vec::new();
for entity in queued_text.entities.drain(..) {
if let Ok(mut result) = text_query.entity(entity) {
if let Some((text, mut calculated_size)) = result.get() {
let font_atlases = font_atlas_sets
.get_or_insert_with(text.font.id, || FontAtlasSet::new(text.font.clone_weak()));
// TODO: this call results in one or more TextureAtlases, whose render resources are created in the RENDER_GRAPH_SYSTEMS
// stage. That logic runs _before_ the DRAW stage, which means we cant call add_glyphs_to_atlas in the draw stage
// without our render resources being a frame behind. Therefore glyph atlasing either needs its own system or the TextureAtlas
// resource generation needs to happen AFTER the render graph systems. maybe draw systems should execute within the
// render graph so ordering like this can be taken into account? Maybe the RENDER_GRAPH_SYSTEMS stage should be removed entirely
// in favor of node.update()? Regardless, in the immediate short term the current approach is fine.
if let Some(width) = font_atlases.add_glyphs_to_atlas(
&fonts,
&mut texture_atlases,
&mut textures,
text.style.font_size,
&text.value,
) {
calculated_size.size = Size::new(width, text.style.font_size);
} else {
new_queued_text.push(entity);
}
if let Ok((text, mut calculated_size)) = queries.q1_mut().entity_mut(entity) {
let font_atlases = font_atlas_sets
.get_or_insert_with(text.font.id, || FontAtlasSet::new(text.font.clone_weak()));
// TODO: this call results in one or more TextureAtlases, whose render resources are created in the RENDER_GRAPH_SYSTEMS
// stage. That logic runs _before_ the DRAW stage, which means we cant call add_glyphs_to_atlas in the draw stage
// without our render resources being a frame behind. Therefore glyph atlasing either needs its own system or the TextureAtlas
// resource generation needs to happen AFTER the render graph systems. maybe draw systems should execute within the
// render graph so ordering like this can be taken into account? Maybe the RENDER_GRAPH_SYSTEMS stage should be removed entirely
// in favor of node.update()? Regardless, in the immediate short term the current approach is fine.
if let Some(width) = font_atlases.add_glyphs_to_atlas(
&fonts,
&mut texture_atlases,
&mut textures,
text.style.font_size,
&text.value,
) {
calculated_size.size = Size::new(width, text.style.font_size);
} else {
new_queued_text.push(entity);
}
}
}
@ -64,7 +64,7 @@ pub fn text_system(
queued_text.entities = new_queued_text;
// add changed text to atlases
for (entity, text, mut calculated_size) in &mut query.iter() {
for (entity, text, mut calculated_size) in queries.q0_mut().iter_mut() {
let font_atlases = font_atlas_sets
.get_or_insert_with(text.font.id, || FontAtlasSet::new(text.font.clone_weak()));
// TODO: this call results in one or more TextureAtlases, whose render resources are created in the RENDER_GRAPH_SYSTEMS
@ -98,7 +98,7 @@ pub fn draw_text_system(
mut asset_render_resource_bindings: ResMut<AssetRenderResourceBindings>,
mut query: Query<(&mut Draw, &Text, &Node, &GlobalTransform)>,
) {
for (mut draw, text, node, global_transform) in &mut query.iter() {
for (mut draw, text, node, global_transform) in query.iter_mut() {
if let Some(font) = fonts.get(&text.font) {
let position = global_transform.translation - (node.size / 2.0).extend(0.0);
let mut drawable_text = DrawableText {

View File

@ -12,7 +12,7 @@ fn animate_sprite_system(
texture_atlases: Res<Assets<TextureAtlas>>,
mut query: Query<(&mut Timer, &mut TextureAtlasSprite, &Handle<TextureAtlas>)>,
) {
for (timer, mut sprite, texture_atlas_handle) in &mut query.iter() {
for (timer, mut sprite, texture_atlas_handle) in query.iter_mut() {
if timer.finished {
let texture_atlas = texture_atlases.get(texture_atlas_handle).unwrap();
sprite.index = ((sprite.index as usize + 1) % texture_atlas.textures.len()) as u32;

View File

@ -16,7 +16,7 @@ struct Rotator;
/// rotates the parent, which will result in the child also rotating
fn rotator_system(time: Res<Time>, mut query: Query<(&Rotator, &mut Transform)>) {
for (_rotator, mut transform) in &mut query.iter() {
for (_rotator, mut transform) in query.iter_mut() {
transform.rotation *= Quat::from_rotation_x(3.0 * time.delta_seconds);
}
}

View File

@ -23,7 +23,7 @@ fn move_cubes(
mut materials: ResMut<Assets<StandardMaterial>>,
mut query: Query<(&mut Transform, &Handle<StandardMaterial>)>,
) {
for (mut transform, material_handle) in &mut query.iter() {
for (mut transform, material_handle) in query.iter_mut() {
let material = materials.get_mut(material_handle).unwrap();
transform.translation += Vec3::new(1.0, 0.0, 0.0) * time.delta_seconds;
material.albedo =

View File

@ -20,17 +20,17 @@ struct Rotator;
/// rotates the parent, which will result in the child also rotating
fn rotator_system(time: Res<Time>, mut query: Query<(&Rotator, &mut Transform)>) {
for (_rotator, mut transform) in &mut query.iter() {
for (_rotator, mut transform) in query.iter_mut() {
transform.rotation *= Quat::from_rotation_x(3.0 * time.delta_seconds);
}
}
fn camera_order_color_system(
mut materials: ResMut<Assets<StandardMaterial>>,
mut camera_query: Query<(&Camera, &VisibleEntities)>,
camera_query: Query<(&Camera, &VisibleEntities)>,
material_query: Query<&Handle<StandardMaterial>>,
) {
for (_camera, visible_entities) in &mut camera_query.iter() {
for (_camera, visible_entities) in camera_query.iter() {
for visible_entity in visible_entities.iter() {
if let Ok(material_handle) =
material_query.get::<Handle<StandardMaterial>>(visible_entity.entity)

View File

@ -85,7 +85,7 @@ fn new_round_system(game_rules: Res<GameRules>, mut game_state: ResMut<GameState
// This system updates the score for each entity with the "Player" and "Score" component.
fn score_system(mut query: Query<(&Player, &mut Score)>) {
for (player, mut score) in &mut query.iter() {
for (player, mut score) in query.iter_mut() {
let scored_a_point = random::<bool>();
if scored_a_point {
score.value += 1;
@ -110,9 +110,9 @@ fn score_system(mut query: Query<(&Player, &mut Score)>) {
fn score_check_system(
game_rules: Res<GameRules>,
mut game_state: ResMut<GameState>,
mut query: Query<(&Player, &Score)>,
query: Query<(&Player, &Score)>,
) {
for (player, score) in &mut query.iter() {
for (player, score) in query.iter() {
if score.value == game_rules.winning_score {
game_state.winning_player = Some(player.name.clone());
}
@ -237,8 +237,8 @@ struct State {
// NOTE: this doesn't do anything relevant to our game, it is just here for illustrative purposes
#[allow(dead_code)]
fn local_state_system(mut state: Local<State>, mut query: Query<(&Player, &Score)>) {
for (player, score) in &mut query.iter() {
fn local_state_system(mut state: Local<State>, query: Query<(&Player, &Score)>) {
for (player, score) in query.iter() {
println!("processed: {} {}", player.name, score.value);
}
println!("this system ran {} times", state.counter);

View File

@ -91,18 +91,21 @@ fn setup(
fn rotate(
mut commands: Commands,
time: Res<Time>,
mut parents_query: Query<(Entity, &mut Transform, &mut Children, &Sprite)>,
children_query: Query<(&mut Transform, &Sprite)>,
mut parents_query: Query<(Entity, &mut Children, &Sprite)>,
mut transform_query: Query<With<Sprite, &mut Transform>>,
) {
let angle = std::f32::consts::PI / 2.0;
for (parent, mut transform, mut children, _) in &mut parents_query.iter() {
transform.rotate(Quat::from_rotation_z(-angle * time.delta_seconds));
for (parent, mut children, _) in parents_query.iter_mut() {
if let Ok(mut transform) = transform_query.entity_mut(parent) {
transform.rotate(Quat::from_rotation_z(-angle * time.delta_seconds));
}
// To iterate through the entities children, just treat the Children component as a Vec
// Alternatively, you could query entities that have a Parent component
for child in children.iter() {
let mut transform = children_query.get_mut::<Transform>(*child).unwrap();
transform.rotate(Quat::from_rotation_z(angle * 2.0 * time.delta_seconds));
if let Ok(mut transform) = transform_query.entity_mut(*child) {
transform.rotate(Quat::from_rotation_z(angle * 2.0 * time.delta_seconds));
}
}
// To demonstrate removing children, we'll start to remove the children after a couple of seconds
@ -115,7 +118,6 @@ fn rotate(
}
if time.seconds_since_startup >= 4.0 {
// Alternatively, you can use .despawn_recursive()
// This will remove the entity from its parent's list of children, as well as despawn
// any children the entity has.
commands.despawn_recursive(parent);

View File

@ -35,8 +35,7 @@ fn move_system(pool: Res<ComputeTaskPool>, mut sprites: Query<(&mut Transform, &
// See the ParallelIterator documentation for more information on when
// to use or not use ParallelIterator over a normal Iterator.
sprites
.iter()
.par_iter(32)
.par_iter_mut(32)
.for_each(&pool, |(mut transform, velocity)| {
transform.translation += velocity.0.extend(0.0);
});
@ -56,10 +55,9 @@ fn bounce_system(
let bottom = height as f32 / -2.0;
let top = height as f32 / 2.0;
sprites
.iter()
// Batch size of 32 is chosen to limit the overhead of
// ParallelIterator, since negating a vector is very inexpensive.
.par_iter(32)
.par_iter_mut(32)
// Filter out sprites that don't need to be bounced
.filter(|(transform, _)| {
!(left < transform.translation.x()

View File

@ -161,7 +161,7 @@ fn paddle_movement_system(
keyboard_input: Res<Input<KeyCode>>,
mut query: Query<(&Paddle, &mut Transform)>,
) {
for (paddle, mut transform) in &mut query.iter() {
for (paddle, mut transform) in query.iter_mut() {
let mut direction = 0.0;
if keyboard_input.pressed(KeyCode::Left) {
direction -= 1.0;
@ -183,13 +183,13 @@ fn ball_movement_system(time: Res<Time>, mut ball_query: Query<(&Ball, &mut Tran
// clamp the timestep to stop the ball from escaping when the game starts
let delta_seconds = f32::min(0.2, time.delta_seconds);
for (ball, mut transform) in &mut ball_query.iter() {
for (ball, mut transform) in ball_query.iter_mut() {
transform.translation += ball.velocity * delta_seconds;
}
}
fn scoreboard_system(scoreboard: Res<Scoreboard>, mut query: Query<&mut Text>) {
for mut text in &mut query.iter() {
for mut text in query.iter_mut() {
text.value = format!("Score: {}", scoreboard.score);
}
}
@ -198,14 +198,14 @@ fn ball_collision_system(
mut commands: Commands,
mut scoreboard: ResMut<Scoreboard>,
mut ball_query: Query<(&mut Ball, &Transform, &Sprite)>,
mut collider_query: Query<(Entity, &Collider, &Transform, &Sprite)>,
collider_query: Query<(Entity, &Collider, &Transform, &Sprite)>,
) {
for (mut ball, ball_transform, sprite) in &mut ball_query.iter() {
for (mut ball, ball_transform, sprite) in ball_query.iter_mut() {
let ball_size = sprite.size;
let velocity = &mut ball.velocity;
// check collision with walls
for (collider_entity, collider, transform, sprite) in &mut collider_query.iter() {
for (collider_entity, collider, transform, sprite) in collider_query.iter() {
let collision = collide(
ball_transform.translation,
ball_size,

View File

@ -65,8 +65,8 @@ fn load_scene_system(asset_server: Res<AssetServer>, mut scene_spawner: ResMut<S
// This system prints all ComponentA components in our world. Try making a change to a ComponentA in load_scene_example.scn.
// You should immediately see the changes appear in the console.
fn print_system(mut query: Query<(Entity, Changed<ComponentA>)>) {
for (entity, component_a) in &mut query.iter() {
fn print_system(query: Query<(Entity, Changed<ComponentA>)>) {
for (entity, component_a) in query.iter() {
println!(" Entity({})", entity.id());
println!(
" ComponentA: {{ x: {} y: {} }}\n",

View File

@ -35,9 +35,9 @@ fn button_system(
&mut Handle<ColorMaterial>,
&Children,
)>,
text_query: Query<&mut Text>,
mut text_query: Query<&mut Text>,
) {
for (_button, interaction, mut material, children) in &mut interaction_query.iter() {
for (_button, interaction, mut material, children) in interaction_query.iter_mut() {
let mut text = text_query.get_mut::<Text>(children[0]).unwrap();
match *interaction {
Interaction::Clicked => {

View File

@ -62,7 +62,7 @@ fn atlas_render_system(
}
fn text_update_system(mut state: ResMut<State>, time: Res<Time>, mut query: Query<&mut Text>) {
for mut text in &mut query.iter() {
for mut text in query.iter_mut() {
state.timer.tick(time.delta_seconds);
let c = rand::random::<u8>() as char;
if !text.value.contains(c) && state.timer.finished {

View File

@ -17,7 +17,7 @@ fn main() {
struct FpsText;
fn text_update_system(diagnostics: Res<Diagnostics>, mut query: Query<(&mut Text, &FpsText)>) {
for (mut text, _tag) in &mut query.iter() {
for (mut text, _tag) in query.iter_mut() {
if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) {
if let Some(average) = fps.average() {
text.value = format!("FPS: {:.2}", average);