port upstream hecs performance improvements (#716)
This commit is contained in:
parent
c743a96fe3
commit
a6ac8faa8a
@ -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>>;
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user