ecs: ergonomic query.iter(), remove locks, add QuerySets (#741)
This commit is contained in:
parent
f41f2069a0
commit
1d4a95db62
@ -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)
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
364
crates/bevy_ecs/hecs/src/access.rs
Normal file
364
crates/bevy_ecs/hecs/src/access.rs
Normal 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])
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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> {}
|
||||
@ -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`
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
]
|
||||
);
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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![]);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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> {}
|
||||
203
crates/bevy_ecs/src/system/query/mod.rs
Normal file
203
crates/bevy_ecs/src/system/query/mod.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
25
crates/bevy_ecs/src/system/query/query_set.rs
Normal file
25
crates/bevy_ecs/src/system/query/query_set.rs
Normal 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) },
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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))
|
||||
{
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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<_>>();
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 =
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user