port upstream hecs performance improvements (#716)

This commit is contained in:
Carter Anderson 2020-10-22 11:53:59 -07:00 committed by GitHub
parent c743a96fe3
commit a6ac8faa8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 193 additions and 156 deletions

View File

@ -21,13 +21,15 @@ use crate::{
}, },
Entity, Entity,
}; };
use bevy_utils::{HashMap, HashMapExt}; use bevy_utils::AHasher;
use core::{ use core::{
any::{type_name, TypeId}, any::{type_name, TypeId},
cell::UnsafeCell, cell::UnsafeCell,
hash::{BuildHasherDefault, Hasher},
mem, mem,
ptr::{self, NonNull}, ptr::{self, NonNull},
}; };
use std::collections::HashMap;
use crate::{borrow::AtomicBorrow, query::Fetch, Access, Component, Query}; use crate::{borrow::AtomicBorrow, query::Fetch, Access, Component, Query};
@ -38,7 +40,7 @@ use crate::{borrow::AtomicBorrow, query::Fetch, Access, Component, Query};
#[derive(Debug)] #[derive(Debug)]
pub struct Archetype { pub struct Archetype {
types: Vec<TypeInfo>, types: Vec<TypeInfo>,
state: HashMap<TypeId, TypeState>, state: TypeIdMap<TypeState>,
len: usize, len: usize,
entities: Vec<Entity>, entities: Vec<Entity>,
// UnsafeCell allows unique references into `data` to be constructed while shared references // UnsafeCell allows unique references into `data` to be constructed while shared references
@ -60,7 +62,7 @@ impl Archetype {
types.windows(2).all(|x| x[0] < x[1]), types.windows(2).all(|x| x[0] < x[1]),
"type info unsorted or contains duplicates" "type info unsorted or contains duplicates"
); );
let mut state = HashMap::with_capacity(types.len()); let mut state = HashMap::with_capacity_and_hasher(types.len(), Default::default());
for ty in &types { for ty in &types {
state.insert(ty.id, TypeState::new()); state.insert(ty.id, TypeState::new());
} }
@ -549,3 +551,47 @@ fn align(x: usize, alignment: usize) -> usize {
debug_assert!(alignment.is_power_of_two()); debug_assert!(alignment.is_power_of_two());
(x + alignment - 1) & (!alignment + 1) (x + alignment - 1) & (!alignment + 1)
} }
/// A hasher optimized for hashing a single TypeId.
///
/// TypeId is already thoroughly hashed, so there's no reason to hash it again.
/// Just leave the bits unchanged.
#[derive(Default)]
pub(crate) struct TypeIdHasher {
hash: u64,
}
impl Hasher for TypeIdHasher {
fn write_u64(&mut self, n: u64) {
// Only a single value can be hashed, so the old hash should be zero.
debug_assert_eq!(self.hash, 0);
self.hash = n;
}
// Tolerate TypeId being either u64 or u128.
fn write_u128(&mut self, n: u128) {
debug_assert_eq!(self.hash, 0);
self.hash = n as u64;
}
fn write(&mut self, bytes: &[u8]) {
debug_assert_eq!(self.hash, 0);
// This will only be called if TypeId is neither u64 nor u128, which is not anticipated.
// In that case we'll just fall back to using a different hash implementation.
let mut hasher = AHasher::default();
hasher.write(bytes);
self.hash = hasher.finish();
}
fn finish(&self) -> u64 {
self.hash
}
}
/// A HashMap with TypeId keys
///
/// Because TypeId is already a fully-hashed u64 (including data in the high seven bits,
/// which hashbrown needs), there is no need to hash it again. Instead, this uses the much
/// faster no-op hash.
pub(crate) type TypeIdMap<V> = HashMap<TypeId, V, BuildHasherDefault<TypeIdHasher>>;

View File

@ -36,6 +36,10 @@ pub trait Fetch<'a>: Sized {
/// Type of value to be fetched /// Type of value to be fetched
type Item; type Item;
/// A value on which `get` may never be called
#[allow(clippy::declare_interior_mutable_const)] // no const fn in traits
const DANGLING: Self;
/// How this query will access `archetype`, if at all /// How this query will access `archetype`, if at all
fn access(archetype: &Archetype) -> Option<Access>; fn access(archetype: &Archetype) -> Option<Access>;
@ -49,22 +53,22 @@ pub trait Fetch<'a>: Sized {
/// Release dynamic borrows acquired by `borrow` /// Release dynamic borrows acquired by `borrow`
fn release(archetype: &Archetype); fn release(archetype: &Archetype);
/// if this returns true, the current item will be skipped during iteration /// if this returns true, the nth item should be skipped during iteration
/// ///
/// # Safety /// # Safety
/// shouldn't be called if there is no current item /// shouldn't be called if there is no current item
unsafe fn should_skip(&self) -> bool { unsafe fn should_skip(&self, _n: usize) -> bool {
false false
} }
/// Access the next item in this archetype without bounds checking /// Access the `n`th item in this archetype without bounds checking
/// ///
/// # Safety /// # Safety
/// - Must only be called after `borrow` /// - Must only be called after `borrow`
/// - `release` must not be called while `'a` is still live /// - `release` must not be called while `'a` is still live
/// - Bounds-checking must be performed externally /// - Bounds-checking must be performed externally
/// - Any resulting borrows must be legal (e.g. no &mut to something another iterator might access) /// - Any resulting borrows must be legal (e.g. no &mut to something another iterator might access)
unsafe fn next(&mut self) -> Self::Item; unsafe fn fetch(&self, n: usize) -> Self::Item;
} }
/// Type of access a `Query` may have to an `Archetype` /// Type of access a `Query` may have to an `Archetype`
@ -89,6 +93,8 @@ impl Query for Entity {
impl<'a> Fetch<'a> for EntityFetch { impl<'a> Fetch<'a> for EntityFetch {
type Item = Entity; type Item = Entity;
const DANGLING: Self = Self(NonNull::dangling());
#[inline] #[inline]
fn access(_archetype: &Archetype) -> Option<Access> { fn access(_archetype: &Archetype) -> Option<Access> {
Some(Access::Iterate) Some(Access::Iterate)
@ -108,10 +114,8 @@ impl<'a> Fetch<'a> for EntityFetch {
fn release(_archetype: &Archetype) {} fn release(_archetype: &Archetype) {}
#[inline] #[inline]
unsafe fn next(&mut self) -> Self::Item { unsafe fn fetch(&self, n: usize) -> Self::Item {
let id = self.0.as_ptr(); *self.0.as_ptr().add(n)
self.0 = NonNull::new_unchecked(id.add(1));
*id
} }
} }
@ -127,6 +131,8 @@ unsafe impl<T> ReadOnlyFetch for FetchRead<T> {}
impl<'a, T: Component> Fetch<'a> for FetchRead<T> { impl<'a, T: Component> Fetch<'a> for FetchRead<T> {
type Item = &'a T; type Item = &'a T;
const DANGLING: Self = Self(NonNull::dangling());
fn access(archetype: &Archetype) -> Option<Access> { fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() { if archetype.has::<T>() {
Some(Access::Read) Some(Access::Read)
@ -150,10 +156,8 @@ impl<'a, T: Component> Fetch<'a> for FetchRead<T> {
} }
#[inline] #[inline]
unsafe fn next(&mut self) -> &'a T { unsafe fn fetch(&self, n: usize) -> &'a T {
let x = self.0.as_ptr(); &*self.0.as_ptr().add(n)
self.0 = NonNull::new_unchecked(x.add(1));
&*x
} }
} }
@ -222,6 +226,8 @@ pub struct FetchMut<T>(NonNull<T>, NonNull<bool>);
impl<'a, T: Component> Fetch<'a> for FetchMut<T> { impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
type Item = Mut<'a, T>; type Item = Mut<'a, T>;
const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling());
fn access(archetype: &Archetype) -> Option<Access> { fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() { if archetype.has::<T>() {
Some(Access::Write) Some(Access::Write)
@ -250,14 +256,10 @@ impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
} }
#[inline] #[inline]
unsafe fn next(&mut self) -> Mut<'a, T> { unsafe fn fetch(&self, n: usize) -> Mut<'a, T> {
let component = self.0.as_ptr();
let mutated = self.1.as_ptr();
self.0 = NonNull::new_unchecked(component.add(1));
self.1 = NonNull::new_unchecked(mutated.add(1));
Mut { Mut {
value: &mut *component, value: &mut *self.0.as_ptr().add(n),
mutated: &mut *mutated, mutated: &mut *self.1.as_ptr().add(n),
} }
} }
} }
@ -271,6 +273,8 @@ macro_rules! impl_or_query {
impl<'a, $( $T: Fetch<'a> ),+> Fetch<'a> for FetchOr<($( $T ),+)> { impl<'a, $( $T: Fetch<'a> ),+> Fetch<'a> for FetchOr<($( $T ),+)> {
type Item = ($( $T::Item ),+); type Item = ($( $T::Item ),+);
const DANGLING: Self = Self(($( $T::DANGLING ),+));
fn access(archetype: &Archetype) -> Option<Access> { fn access(archetype: &Archetype) -> Option<Access> {
let mut max_access = None; let mut max_access = None;
$( $(
@ -296,15 +300,15 @@ macro_rules! impl_or_query {
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe fn next(&mut self) -> Self::Item { unsafe fn fetch(&self, n: usize) -> Self::Item {
let ($( $T ),+) = &mut self.0; let ($( $T ),+) = &self.0;
($( $T.next() ),+) ($( $T.fetch(n) ),+)
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe fn should_skip(&self) -> bool { unsafe fn should_skip(&self, n: usize) -> bool {
let ($( $T ),+) = &self.0; let ($( $T ),+) = &self.0;
true $( && $T.should_skip() )+ true $( && $T.should_skip(n) )+
} }
} }
}; };
@ -369,6 +373,8 @@ pub struct FetchMutated<T>(NonNull<T>, NonNull<bool>);
impl<'a, T: Component> Fetch<'a> for FetchMutated<T> { impl<'a, T: Component> Fetch<'a> for FetchMutated<T> {
type Item = Mutated<'a, T>; type Item = Mutated<'a, T>;
const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling());
fn access(archetype: &Archetype) -> Option<Access> { fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() { if archetype.has::<T>() {
Some(Access::Read) Some(Access::Read)
@ -396,17 +402,16 @@ impl<'a, T: Component> Fetch<'a> for FetchMutated<T> {
archetype.release::<T>(); archetype.release::<T>();
} }
unsafe fn should_skip(&self) -> bool { unsafe fn should_skip(&self, n: usize) -> bool {
// skip if the current item wasn't mutated // skip if the current item wasn't mutated
!*self.1.as_ref() !*self.1.as_ptr().add(n)
} }
#[inline] #[inline]
unsafe fn next(&mut self) -> Self::Item { unsafe fn fetch(&self, n: usize) -> Self::Item {
self.1 = NonNull::new_unchecked(self.1.as_ptr().add(1)); Mutated {
let value = self.0.as_ptr(); value: &*self.0.as_ptr().add(n),
self.0 = NonNull::new_unchecked(value.add(1)); }
Mutated { value: &*value }
} }
} }
@ -435,6 +440,8 @@ unsafe impl<T> ReadOnlyFetch for FetchAdded<T> {}
impl<'a, T: Component> Fetch<'a> for FetchAdded<T> { impl<'a, T: Component> Fetch<'a> for FetchAdded<T> {
type Item = Added<'a, T>; type Item = Added<'a, T>;
const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling());
fn access(archetype: &Archetype) -> Option<Access> { fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() { if archetype.has::<T>() {
Some(Access::Read) Some(Access::Read)
@ -462,17 +469,16 @@ impl<'a, T: Component> Fetch<'a> for FetchAdded<T> {
archetype.release::<T>(); archetype.release::<T>();
} }
unsafe fn should_skip(&self) -> bool { unsafe fn should_skip(&self, n: usize) -> bool {
// skip if the current item wasn't added // skip if the current item wasn't added
!*self.1.as_ref() !*self.1.as_ptr().add(n)
} }
#[inline] #[inline]
unsafe fn next(&mut self) -> Self::Item { unsafe fn fetch(&self, n: usize) -> Self::Item {
self.1 = NonNull::new_unchecked(self.1.as_ptr().add(1)); Added {
let value = self.0.as_ptr(); value: &*self.0.as_ptr().add(n),
self.0 = NonNull::new_unchecked(value.add(1)); }
Added { value: &*value }
} }
} }
@ -501,6 +507,12 @@ unsafe impl<T> ReadOnlyFetch for FetchChanged<T> {}
impl<'a, T: Component> Fetch<'a> for FetchChanged<T> { impl<'a, T: Component> Fetch<'a> for FetchChanged<T> {
type Item = Changed<'a, T>; type Item = Changed<'a, T>;
const DANGLING: Self = Self(
NonNull::dangling(),
NonNull::dangling(),
NonNull::dangling(),
);
fn access(archetype: &Archetype) -> Option<Access> { fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() { if archetype.has::<T>() {
Some(Access::Read) Some(Access::Read)
@ -529,18 +541,16 @@ impl<'a, T: Component> Fetch<'a> for FetchChanged<T> {
archetype.release::<T>(); archetype.release::<T>();
} }
unsafe fn should_skip(&self) -> bool { unsafe fn should_skip(&self, n: usize) -> bool {
// skip if the current item wasn't added or mutated // skip if the current item wasn't added or mutated
!*self.1.as_ref() && !self.2.as_ref() !*self.1.as_ptr().add(n) && !*self.2.as_ptr().add(n)
} }
#[inline] #[inline]
unsafe fn next(&mut self) -> Self::Item { unsafe fn fetch(&self, n: usize) -> Self::Item {
self.1 = NonNull::new_unchecked(self.1.as_ptr().add(1)); Changed {
self.2 = NonNull::new_unchecked(self.2.as_ptr().add(1)); value: &*self.0.as_ptr().add(n),
let value = self.0.as_ptr(); }
self.0 = NonNull::new_unchecked(value.add(1));
Changed { value: &*value }
} }
} }
@ -551,6 +561,8 @@ unsafe impl<T> ReadOnlyFetch for TryFetch<T> where T: ReadOnlyFetch {}
impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> { impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
type Item = Option<T::Item>; type Item = Option<T::Item>;
const DANGLING: Self = Self(None);
fn access(archetype: &Archetype) -> Option<Access> { fn access(archetype: &Archetype) -> Option<Access> {
Some(T::access(archetype).unwrap_or(Access::Iterate)) Some(T::access(archetype).unwrap_or(Access::Iterate))
} }
@ -567,12 +579,12 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
T::release(archetype) T::release(archetype)
} }
unsafe fn next(&mut self) -> Option<T::Item> { unsafe fn fetch(&self, n: usize) -> Option<T::Item> {
Some(self.0.as_mut()?.next()) Some(self.0.as_ref()?.fetch(n))
} }
unsafe fn should_skip(&self) -> bool { unsafe fn should_skip(&self, n: usize) -> bool {
self.0.as_ref().map_or(false, |fetch| fetch.should_skip()) self.0.as_ref().map_or(false, |fetch| fetch.should_skip(n))
} }
} }
@ -609,6 +621,8 @@ unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWithout<T, F>
impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout<T, F> { impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout<T, F> {
type Item = F::Item; type Item = F::Item;
const DANGLING: Self = Self(F::DANGLING, PhantomData);
fn access(archetype: &Archetype) -> Option<Access> { fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() { if archetype.has::<T>() {
None None
@ -632,12 +646,12 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout<T, F> {
F::release(archetype) F::release(archetype)
} }
unsafe fn next(&mut self) -> F::Item { unsafe fn fetch(&self, n: usize) -> F::Item {
self.0.next() self.0.fetch(n)
} }
unsafe fn should_skip(&self) -> bool { unsafe fn should_skip(&self, n: usize) -> bool {
self.0.should_skip() self.0.should_skip(n)
} }
} }
@ -673,6 +687,8 @@ unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWith<T, F> wh
impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith<T, F> { impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith<T, F> {
type Item = F::Item; type Item = F::Item;
const DANGLING: Self = Self(F::DANGLING, PhantomData);
fn access(archetype: &Archetype) -> Option<Access> { fn access(archetype: &Archetype) -> Option<Access> {
if archetype.has::<T>() { if archetype.has::<T>() {
F::access(archetype) F::access(archetype)
@ -696,12 +712,12 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith<T, F> {
F::release(archetype) F::release(archetype)
} }
unsafe fn next(&mut self) -> F::Item { unsafe fn fetch(&self, n: usize) -> F::Item {
self.0.next() self.0.fetch(n)
} }
unsafe fn should_skip(&self) -> bool { unsafe fn should_skip(&self, n: usize) -> bool {
self.0.should_skip() self.0.should_skip(n)
} }
} }
@ -731,7 +747,7 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> {
QueryIter { QueryIter {
borrow: self, borrow: self,
archetype_index: 0, archetype_index: 0,
iter: None, iter: ChunkIter::EMPTY,
} }
} }
@ -835,7 +851,7 @@ impl<'q, 'w, Q: Query> IntoIterator for &'q mut QueryBorrow<'w, Q> {
pub struct QueryIter<'q, 'w, Q: Query> { pub struct QueryIter<'q, 'w, Q: Query> {
borrow: &'q mut QueryBorrow<'w, Q>, borrow: &'q mut QueryBorrow<'w, Q>,
archetype_index: usize, archetype_index: usize,
iter: Option<ChunkIter<Q>>, iter: ChunkIter<Q>,
} }
unsafe impl<'q, 'w, Q: Query> Send for QueryIter<'q, 'w, Q> {} unsafe impl<'q, 'w, Q: Query> Send for QueryIter<'q, 'w, Q> {}
@ -847,26 +863,21 @@ impl<'q, 'w, Q: Query> Iterator for QueryIter<'q, 'w, Q> {
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
loop { loop {
match self.iter { match unsafe { self.iter.next() } {
None => { None => {
let archetype = self.borrow.archetypes.get(self.archetype_index)?; let archetype = self.borrow.archetypes.get(self.archetype_index)?;
self.archetype_index += 1; self.archetype_index += 1;
unsafe { unsafe {
self.iter = Q::Fetch::get(archetype, 0).map(|fetch| ChunkIter { self.iter = Q::Fetch::get(archetype, 0).map_or(ChunkIter::EMPTY, |fetch| {
fetch, ChunkIter {
len: archetype.len(), fetch,
len: archetype.len(),
position: 0,
}
}); });
} }
} }
Some(ref mut iter) => match unsafe { iter.next() } { Some(components) => return Some(components),
None => {
self.iter = None;
continue;
}
Some(components) => {
return Some(components);
}
},
} }
} }
} }
@ -890,24 +901,32 @@ impl<'q, 'w, Q: Query> ExactSizeIterator for QueryIter<'q, 'w, Q> {
struct ChunkIter<Q: Query> { struct ChunkIter<Q: Query> {
fetch: Q::Fetch, fetch: Q::Fetch,
position: usize,
len: usize, len: usize,
} }
impl<Q: Query> ChunkIter<Q> { 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> { unsafe fn next<'a>(&mut self) -> Option<<Q::Fetch as Fetch<'a>>::Item> {
loop { loop {
if self.len == 0 { if self.position == self.len {
return None; return None;
} }
self.len -= 1; if self.fetch.should_skip(self.position as usize) {
if self.fetch.should_skip() { self.position += 1;
// we still need to progress the iterator
let _ = self.fetch.next();
continue; continue;
} }
break Some(self.fetch.next()); let item = Some(self.fetch.fetch(self.position as usize));
self.position += 1;
return item;
} }
} }
} }
@ -941,6 +960,7 @@ impl<'q, 'w, Q: Query> Iterator for BatchedIter<'q, 'w, Q> {
_marker: PhantomData, _marker: PhantomData,
state: ChunkIter { state: ChunkIter {
fetch, fetch,
position: 0,
len: self.batch_size.min(archetype.len() - offset), len: self.batch_size.min(archetype.len() - offset),
}, },
}); });
@ -978,6 +998,7 @@ macro_rules! tuple_impl {
($($name: ident),*) => { ($($name: ident),*) => {
impl<'a, $($name: Fetch<'a>),*> Fetch<'a> for ($($name,)*) { impl<'a, $($name: Fetch<'a>),*> Fetch<'a> for ($($name,)*) {
type Item = ($($name::Item,)*); type Item = ($($name::Item,)*);
const DANGLING: Self = ($($name::DANGLING,)*);
#[allow(unused_variables, unused_mut)] #[allow(unused_variables, unused_mut)]
fn access(archetype: &Archetype) -> Option<Access> { fn access(archetype: &Archetype) -> Option<Access> {
@ -1002,16 +1023,17 @@ macro_rules! tuple_impl {
} }
#[allow(unused_variables)] #[allow(unused_variables)]
unsafe fn next(&mut self) -> Self::Item { unsafe fn fetch(&self, n: usize) -> Self::Item {
#[allow(non_snake_case)] #[allow(non_snake_case)]
let ($($name,)*) = self; let ($($name,)*) = self;
($($name.next(),)*) ($($name.fetch(n),)*)
} }
unsafe fn should_skip(&self) -> bool { #[allow(unused_variables)]
unsafe fn should_skip(&self, n: usize) -> bool {
#[allow(non_snake_case)] #[allow(non_snake_case)]
let ($($name,)*) = self; let ($($name,)*) = self;
$($name.should_skip()||)* false $($name.should_skip(n)||)* false
} }
} }

View File

@ -36,11 +36,11 @@ impl<'a, Q: Query> QueryOne<'a, Q> {
/// pre-existing borrow. /// pre-existing borrow.
pub fn get(&mut self) -> Option<<Q::Fetch as Fetch<'_>>::Item> { pub fn get(&mut self) -> Option<<Q::Fetch as Fetch<'_>>::Item> {
unsafe { unsafe {
let mut fetch = Q::Fetch::get(self.archetype, self.index)?; let fetch = Q::Fetch::get(self.archetype, self.index)?;
if fetch.should_skip() { if fetch.should_skip(0) {
None None
} else { } else {
Some(fetch.next()) Some(fetch.fetch(0))
} }
} }
} }
@ -107,11 +107,11 @@ where
Q::Fetch: ReadOnlyFetch, Q::Fetch: ReadOnlyFetch,
{ {
unsafe { unsafe {
let mut fetch = Q::Fetch::get(self.archetype, self.index)?; let fetch = Q::Fetch::get(self.archetype, self.index)?;
if fetch.should_skip() { if fetch.should_skip(0) {
None None
} else { } else {
Some(fetch.next()) Some(fetch.fetch(0))
} }
} }
} }

View File

@ -162,7 +162,7 @@ impl<'w, Q: HecsQuery> QueryBorrowChecked<'w, Q> {
QueryIter { QueryIter {
borrow: self, borrow: self,
archetype_index: 0, archetype_index: 0,
iter: None, iter: ChunkIter::EMPTY,
} }
} }
@ -233,20 +233,7 @@ impl<'q, 'w, Q: HecsQuery> IntoIterator for &'q mut QueryBorrowChecked<'w, Q> {
pub struct QueryIter<'q, 'w, Q: HecsQuery> { pub struct QueryIter<'q, 'w, Q: HecsQuery> {
borrow: &'q mut QueryBorrowChecked<'w, Q>, borrow: &'q mut QueryBorrowChecked<'w, Q>,
archetype_index: usize, archetype_index: usize,
iter: Option<ChunkIter<Q>>, iter: ChunkIter<Q>,
}
impl<'q, 'w, Q: HecsQuery> fmt::Debug for QueryIter<'q, 'w, Q>
where
Q::Fetch: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("QueryIter")
.field("borrow", self.borrow)
.field("archetype_index", &self.archetype_index)
.field("iter", &self.iter)
.finish()
}
} }
unsafe impl<'q, 'w, Q: HecsQuery> Send for QueryIter<'q, 'w, Q> {} unsafe impl<'q, 'w, Q: HecsQuery> Send for QueryIter<'q, 'w, Q> {}
@ -258,26 +245,21 @@ impl<'q, 'w, Q: HecsQuery> Iterator for QueryIter<'q, 'w, Q> {
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
loop { loop {
match self.iter { match unsafe { self.iter.next() } {
None => { None => {
let archetype = self.borrow.archetypes.get(self.archetype_index as usize)?; let archetype = self.borrow.archetypes.get(self.archetype_index)?;
self.archetype_index += 1; self.archetype_index += 1;
unsafe { unsafe {
self.iter = Q::Fetch::get(archetype, 0).map(|fetch| ChunkIter { self.iter = Q::Fetch::get(archetype, 0).map_or(ChunkIter::EMPTY, |fetch| {
fetch, ChunkIter {
len: archetype.len(), fetch,
len: archetype.len(),
position: 0,
}
}); });
} }
} }
Some(ref mut iter) => match unsafe { iter.next() } { Some(components) => return Some(components),
None => {
self.iter = None;
continue;
}
Some(components) => {
return Some(components);
}
},
} }
} }
} }
@ -301,41 +283,35 @@ impl<'q, 'w, Q: HecsQuery> ExactSizeIterator for QueryIter<'q, 'w, Q> {
struct ChunkIter<Q: HecsQuery> { struct ChunkIter<Q: HecsQuery> {
fetch: Q::Fetch, fetch: Q::Fetch,
position: usize,
len: usize, len: usize,
} }
impl<Q: HecsQuery> fmt::Debug for ChunkIter<Q>
where
Q::Fetch: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ChunkIter")
.field("fetch", &self.fetch)
.field("len", &self.len)
.finish()
}
}
impl<Q: HecsQuery> ChunkIter<Q> { impl<Q: HecsQuery> ChunkIter<Q> {
#[inline] #[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> { unsafe fn next<'a>(&mut self) -> Option<<Q::Fetch as Fetch<'a>>::Item> {
loop { loop {
if self.len == 0 { if self.position == self.len {
return None; return None;
} }
self.len -= 1; if self.fetch.should_skip(self.position as usize) {
if self.fetch.should_skip() { self.position += 1;
// we still need to progress the iterator
let _ = self.fetch.next();
continue; continue;
} }
break Some(self.fetch.next()); let item = Some(self.fetch.fetch(self.position as usize));
self.position += 1;
return item;
} }
} }
} }
/// Batched version of `QueryIter` /// Batched version of `QueryIter`
pub struct ParIter<'q, 'w, Q: HecsQuery> { pub struct ParIter<'q, 'w, Q: HecsQuery> {
borrow: &'q mut QueryBorrowChecked<'w, Q>, borrow: &'q mut QueryBorrowChecked<'w, Q>,
@ -363,6 +339,7 @@ impl<'q, 'w, Q: HecsQuery> ParallelIterator<Batch<'q, Q>> for ParIter<'q, 'w, Q>
state: ChunkIter { state: ChunkIter {
fetch, fetch,
len: self.batch_size.min(archetype.len() - offset), len: self.batch_size.min(archetype.len() - offset),
position: 0,
}, },
}); });
} else { } else {
@ -383,18 +360,6 @@ pub struct Batch<'q, Q: HecsQuery> {
state: ChunkIter<Q>, state: ChunkIter<Q>,
} }
impl<'q, Q: HecsQuery> fmt::Debug for Batch<'q, Q>
where
Q::Fetch: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Batch")
.field("_marker", &self._marker)
.field("state", &self.state)
.finish()
}
}
impl<'q, 'w, Q: HecsQuery> Iterator for Batch<'q, Q> { impl<'q, 'w, Q: HecsQuery> Iterator for Batch<'q, Q> {
type Item = <Q::Fetch as Fetch<'q>>::Item; type Item = <Q::Fetch as Fetch<'q>>::Item;
@ -438,10 +403,14 @@ impl<'a, Q: HecsQuery> QueryOneChecked<'a, Q> {
/// pre-existing borrow. /// pre-existing borrow.
pub fn get(&mut self) -> Option<<Q::Fetch as Fetch<'_>>::Item> { pub fn get(&mut self) -> Option<<Q::Fetch as Fetch<'_>>::Item> {
unsafe { unsafe {
let mut fetch = Q::Fetch::get(self.archetype, self.index as usize)?; let fetch = Q::Fetch::get(self.archetype, self.index as usize)?;
self.borrowed = true; self.borrowed = true;
Q::Fetch::borrow(self.archetype); if fetch.should_skip(0) {
Some(fetch.next()) None
} else {
Q::Fetch::borrow(self.archetype);
Some(fetch.fetch(0))
}
} }
} }