use std::any::TypeId;
use std::marker::PhantomData;
use std::ptr::NonNull;
#[cfg(feature = "profiling")]
use tracing::info_span;
#[cfg(feature = "profiling")]
#[derive(Debug, Default, Clone, Copy)]
pub struct QueryProfilingData {
pub prepare_time_us: u64,
pub iteration_time_us: u64,
pub total_iterations: u64,
pub avg_iteration_time_us: f64,
}
use crate::archetype::{Archetype, ComponentColumn};
use crate::component::Component;
use crate::entity::EntityId;
use crate::world::World;
use smallvec::{smallvec, SmallVec};
const MAX_FILTER_COMPONENTS: usize = 8;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct QuerySignature {
pub required: SmallVec<[TypeId; 8]>,
pub excluded: SmallVec<[TypeId; 8]>,
}
impl Default for QuerySignature {
fn default() -> Self {
Self::new()
}
}
impl QuerySignature {
pub fn new() -> Self {
Self {
required: smallvec![],
excluded: smallvec![],
}
}
pub fn matches(&self, archetype: &Archetype) -> bool {
for &req in &self.required {
if archetype.column_index(req).is_none() {
return false;
}
}
for &exc in &self.excluded {
if archetype.column_index(exc).is_some() {
return false;
}
}
true
}
}
pub struct CachedQueryResult {
pub matches: Vec<usize>,
pub seen_archetypes: usize,
pub signature: QuerySignature,
}
impl CachedQueryResult {
pub fn new(signature: QuerySignature, archetypes: &[Archetype]) -> Self {
let matched = archetypes
.iter()
.enumerate()
.filter_map(|(id, arch)| {
if signature.matches(arch) {
Some(id)
} else {
None
}
})
.collect();
Self {
matches: matched,
seen_archetypes: archetypes.len(),
signature,
}
}
pub fn update(&mut self, world: &World) {
let count = world.archetype_count();
if self.seen_archetypes < count {
for (id, arch) in world
.archetypes()
.iter()
.enumerate()
.skip(self.seen_archetypes)
{
if self.signature.matches(arch) {
self.matches.push(id);
}
}
self.seen_archetypes = count;
}
}
pub fn iter_simd_chunks<T: Component + Copy>(&mut self, world: &mut World) -> Vec<&mut [T]> {
let mut chunks = Vec::new();
self.update(world);
for &archetype_id in &self.matches {
if let Some(archetype) = world.get_archetype_mut(archetype_id) {
if let Some(col) = archetype.get_column_mut(std::any::TypeId::of::<T>()) {
let len = col.len();
let ptr = col.get_ptr_mut(0) as *mut T;
unsafe {
let slice = std::slice::from_raw_parts_mut(ptr, len);
chunks.extend(crate::simd::chunks(slice));
}
}
}
}
chunks
}
}
pub trait QueryFilter {
fn matches_archetype(archetype: &Archetype) -> bool;
fn type_ids() -> SmallVec<[TypeId; MAX_FILTER_COMPONENTS]>;
fn signature() -> QuerySignature {
let mut sig = QuerySignature::new();
sig.required = Self::type_ids();
sig.required.sort();
sig
}
}
pub struct QueryMut<'w, Q>
where
Q: QueryFilter + QueryFetchMut<'w>,
{
world: &'w mut World,
_phantom: PhantomData<Q>,
}
impl<'w, Q> QueryMut<'w, Q>
where
Q: QueryFilter + QueryFetchMut<'w>,
{
pub fn new(world: &'w mut World) -> Self {
Self {
world,
_phantom: PhantomData,
}
}
pub fn iter(&'w mut self) -> QueryIterMut<'w, Q> {
let matched = self.world.get_cached_query_indices::<Q>();
QueryIterMut::new(self.world, &matched, 0, self.world.tick())
}
pub fn iter_since(&'w mut self, tick: u32) -> QueryIterMut<'w, Q> {
let matched = self.world.get_cached_query_indices::<Q>();
QueryIterMut::new(self.world, &matched, tick, self.world.tick())
}
pub fn count(&mut self) -> usize {
let matched = self.world.get_cached_query_indices::<Q>();
let world_ref: &World = &*self.world;
matched
.iter()
.filter_map(|&id| world_ref.get_archetype(id))
.map(|arch| arch.len())
.sum()
}
#[cfg(feature = "parallel")]
pub fn par_for_each_chunk<F>(&mut self, func: F)
where
F: Fn(crate::archetype::ArchetypeChunkMut) + Send + Sync,
{
use rayon::prelude::*;
let matched = self.world.get_cached_query_indices::<Q>();
let world_ptr = self.world as *mut World as usize;
matched.par_iter().for_each(|&arch_id| {
let world = unsafe { &mut *(world_ptr as *mut World) };
if let Some(archetype) = world.get_archetype_mut(arch_id) {
archetype
.chunks_mut(crate::archetype::DEFAULT_CHUNK_SIZE)
.into_par_iter()
.for_each(&func);
}
});
}
#[cfg(feature = "parallel")]
pub fn par(self) -> ParQuery<'w, Q> {
ParQuery::new(self)
}
}
#[cfg(feature = "parallel")]
pub struct ParQuery<'w, Q>
where
Q: QueryFilter + QueryFetchMut<'w>,
{
query: QueryMut<'w, Q>,
}
#[cfg(feature = "parallel")]
impl<'w, Q> ParQuery<'w, Q>
where
Q: QueryFilter + QueryFetchMut<'w>,
{
pub fn new(query: QueryMut<'w, Q>) -> Self {
Self { query }
}
pub fn for_each<F>(&mut self, func: F)
where
F: Fn(Q::Item) + Send + Sync,
Q: Send + Sync,
Q::Item: Send,
{
use rayon::prelude::*;
let matched = self.query.world.get_cached_query_indices::<Q>();
let current_tick = self.query.world.tick();
let world_cell = unsafe { self.query.world.as_unsafe_world_cell() };
matched.par_iter().for_each(|&arch_id| {
if let Some(archetype_ptr) = unsafe { world_cell.get_archetype_ptr(arch_id) } {
let archetype_w = unsafe { &mut *archetype_ptr.as_ptr() };
let len = archetype_w.len();
if let Some(mut state) = Q::prepare(archetype_w, 0, current_tick) {
for row in 0..len {
if let Some(item) = unsafe { Q::fetch(&mut state, row) } {
func(item);
}
}
}
}
});
}
}
impl<'w, Q> IntoIterator for QueryMut<'w, Q>
where
Q: QueryFilter + QueryFetchMut<'w> + 'w,
{
type Item = <Q as QueryFetchMut<'w>>::Item;
type IntoIter = QueryIterMut<'w, Q>;
fn into_iter(self) -> Self::IntoIter {
let matched = self.world.get_cached_query_indices::<Q>();
QueryIterMut::new(self.world, &matched, 0, self.world.tick())
}
}
pub struct QueryIter<'w, Q: QueryFilter>
where
Q: QueryFetch<'w>,
{
archetypes: Vec<NonNull<Archetype>>,
archetype_index: usize,
entity_index: usize,
change_tick: u32,
state: Option<Q::State>,
_phantom: PhantomData<&'w Q>,
}
impl<'w, Q: QueryFilter> QueryIter<'w, Q>
where
Q: QueryFetch<'w>,
{
fn new(world: &'w World, matched: &[usize], change_tick: u32) -> Self {
let mut archetypes = Vec::with_capacity(matched.len());
for &id in matched {
if let Some(ptr) = world.archetype_ptr(id) {
archetypes.push(ptr);
}
}
Self {
archetypes,
archetype_index: 0,
entity_index: 0,
change_tick,
state: None,
_phantom: PhantomData,
}
}
}
impl<'w, Q> Iterator for QueryIter<'w, Q>
where
Q: QueryFilter + QueryFetch<'w>,
{
type Item = <Q as QueryFetch<'w>>::Item;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.state.is_none() {
if self.archetype_index >= self.archetypes.len() {
return None;
}
let ptr = self.archetypes[self.archetype_index].as_ptr();
self.state = Q::prepare(unsafe { &*ptr }, self.change_tick);
self.entity_index = 0;
if self.state.is_none() {
self.archetype_index += 1;
continue;
}
}
let archetype_ptr = self.archetypes[self.archetype_index].as_ptr();
let archetype = unsafe { &*archetype_ptr };
if self.entity_index >= archetype.len() {
self.state = None;
self.archetype_index += 1;
continue;
}
let row = self.entity_index;
self.entity_index += 1;
if let Some(item) = unsafe { Q::fetch(self.state.as_ref().unwrap(), row) } {
return Some(item);
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.len();
(len, Some(len))
}
}
impl<'w, Q> ExactSizeIterator for QueryIter<'w, Q>
where
Q: QueryFilter + QueryFetch<'w>,
{
fn len(&self) -> usize {
if self.archetype_index >= self.archetypes.len() {
return 0;
}
let mut count = 0;
let current_ptr = self.archetypes[self.archetype_index].as_ptr();
let current = unsafe { &*current_ptr };
count += current.len().saturating_sub(self.entity_index);
for archetype_ptr in self.archetypes.iter().skip(self.archetype_index + 1) {
let archetype = unsafe { &*archetype_ptr.as_ptr() };
count += archetype.len();
}
count
}
}
pub struct QueryIterMut<'w, Q: QueryFilter>
where
Q: QueryFetchMut<'w>,
{
archetypes: Vec<NonNull<Archetype>>,
archetype_index: usize,
entity_index: usize,
#[allow(dead_code)] change_tick: u32,
current_tick: u32,
state: Option<Q::State>,
_phantom: PhantomData<&'w mut Q>,
}
impl<'w, Q: QueryFilter> QueryIterMut<'w, Q>
where
Q: QueryFetchMut<'w>,
{
fn new(world: &'w mut World, matched: &[usize], change_tick: u32, current_tick: u32) -> Self {
let mut archetypes = Vec::with_capacity(matched.len());
for &id in matched {
if let Some(ptr) = world.archetype_ptr_mut(id) {
archetypes.push(ptr);
}
}
Self {
archetypes,
archetype_index: 0,
entity_index: 0,
change_tick,
current_tick,
state: None,
_phantom: PhantomData,
}
}
}
impl<'w, Q> Iterator for QueryIterMut<'w, Q>
where
Q: QueryFilter + QueryFetchMut<'w>,
{
type Item = <Q as QueryFetchMut<'w>>::Item;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.state.is_none() {
if self.archetype_index >= self.archetypes.len() {
return None;
}
let archetype_ptr = self.archetypes[self.archetype_index].as_ptr();
let archetype = unsafe { &mut *archetype_ptr };
self.state = Q::prepare(archetype, self.change_tick, self.current_tick);
self.entity_index = 0;
if self.state.is_none() {
self.archetype_index += 1;
continue; }
}
let archetype_ptr = self.archetypes[self.archetype_index].as_ptr();
let archetype = unsafe { &*archetype_ptr };
if self.entity_index >= archetype.len() {
self.state = None;
self.archetype_index += 1;
continue;
}
let row = self.entity_index;
self.entity_index += 1;
if let Some(item) = unsafe { Q::fetch(self.state.as_mut().unwrap(), row) } {
return Some(item);
}
}
}
}
impl<'w, Q> ExactSizeIterator for QueryIterMut<'w, Q>
where
Q: QueryFilter + QueryFetchMut<'w>,
{
fn len(&self) -> usize {
if self.archetype_index >= self.archetypes.len() {
return 0;
}
let mut count = 0;
let current_ptr = self.archetypes[self.archetype_index].as_ptr();
let current = unsafe { &*current_ptr };
count += current.len().saturating_sub(self.entity_index);
for archetype_ptr in self.archetypes.iter().skip(self.archetype_index + 1) {
let archetype = unsafe { &*archetype_ptr.as_ptr() };
count += archetype.len();
}
count
}
}
pub unsafe trait QueryFetch<'w>: QueryFilter {
type Item;
type State;
fn prepare(archetype: &'w Archetype, change_tick: u32) -> Option<Self::State>;
unsafe fn fetch(state: &Self::State, row: usize) -> Option<Self::Item>;
}
impl<T: Component> QueryFilter for &T {
fn matches_archetype(archetype: &Archetype) -> bool {
archetype.column_index(TypeId::of::<T>()).is_some()
}
fn type_ids() -> SmallVec<[TypeId; MAX_FILTER_COMPONENTS]> {
smallvec![TypeId::of::<T>()]
}
}
unsafe impl<'w, T: Component> QueryFetch<'w> for &'w T {
type Item = &'w T;
type State = &'w ComponentColumn;
fn prepare(archetype: &'w Archetype, _change_tick: u32) -> Option<Self::State> {
let type_id = TypeId::of::<T>();
archetype.get_column(type_id)
}
unsafe fn fetch(state: &Self::State, row: usize) -> Option<Self::Item> {
state.get::<T>(row)
}
}
pub unsafe trait QueryFetchMut<'w>: QueryFilter {
type Item;
type State;
fn prepare(
archetype: &'w mut Archetype,
change_tick: u32,
current_tick: u32,
) -> Option<Self::State>;
unsafe fn fetch(state: &mut Self::State, row: usize) -> Option<Self::Item>;
}
impl<T: Component> QueryFilter for &mut T {
fn matches_archetype(archetype: &Archetype) -> bool {
archetype.column_index(TypeId::of::<T>()).is_some()
}
fn type_ids() -> SmallVec<[TypeId; MAX_FILTER_COMPONENTS]> {
smallvec![TypeId::of::<T>()]
}
}
unsafe impl<'w, T: Component> QueryFetchMut<'w> for &'w mut T {
type Item = &'w mut T;
type State = (*mut ComponentColumn, u32);
fn prepare(
archetype: &'w mut Archetype,
_change_tick: u32,
current_tick: u32,
) -> Option<Self::State> {
let type_id = TypeId::of::<T>();
let column = archetype.get_column_mut(type_id)?;
Some((column as *mut ComponentColumn, current_tick))
}
unsafe fn fetch(state: &mut Self::State, row: usize) -> Option<Self::Item> {
let (column_ptr, current_tick) = state;
let column = unsafe { &mut **column_ptr };
column.set_changed_tick(row, *current_tick);
column.get_mut::<T>(row)
}
}
unsafe impl<'w, T: Component> QueryFetchMut<'w> for &'w T {
type Item = &'w T;
type State = *const ComponentColumn;
fn prepare(
archetype: &'w mut Archetype,
_change_tick: u32,
_current_tick: u32,
) -> Option<Self::State> {
let type_id = TypeId::of::<T>();
archetype
.get_column(type_id)
.map(|col| col as *const ComponentColumn)
}
unsafe fn fetch(state: &mut Self::State, row: usize) -> Option<Self::Item> {
let column = unsafe { &**state };
column.get::<T>(row)
}
}
unsafe impl<'w, A: QueryFetchMut<'w>> QueryFetchMut<'w> for (A,)
where
A: QueryFilter,
{
type Item = (A::Item,);
type State = (A::State,);
fn prepare(
archetype: &'w mut Archetype,
change_tick: u32,
current_tick: u32,
) -> Option<Self::State> {
let state_a = A::prepare(archetype, change_tick, current_tick)?;
Some((state_a,))
}
unsafe fn fetch(state: &mut Self::State, row: usize) -> Option<Self::Item> {
Some((A::fetch(&mut state.0, row)?,))
}
}
unsafe impl<'w, A: QueryFetchMut<'w>, B: QueryFetchMut<'w>> QueryFetchMut<'w> for (A, B)
where
A: QueryFilter,
B: QueryFilter,
{
type Item = (A::Item, B::Item);
type State = (A::State, B::State);
fn prepare(
archetype: &'w mut Archetype,
change_tick: u32,
current_tick: u32,
) -> Option<Self::State> {
let ptr = archetype as *mut Archetype;
let state_a = A::prepare(unsafe { &mut *ptr }, change_tick, current_tick)?;
let state_b = B::prepare(unsafe { &mut *ptr }, change_tick, current_tick)?;
Some((state_a, state_b))
}
unsafe fn fetch(state: &mut Self::State, row: usize) -> Option<Self::Item> {
Some((A::fetch(&mut state.0, row)?, B::fetch(&mut state.1, row)?))
}
}
unsafe impl<'w, A: QueryFetchMut<'w>, B: QueryFetchMut<'w>, C: QueryFetchMut<'w>> QueryFetchMut<'w>
for (A, B, C)
where
A: QueryFilter,
B: QueryFilter,
C: QueryFilter,
{
type Item = (A::Item, B::Item, C::Item);
type State = (A::State, B::State, C::State);
fn prepare(
archetype: &'w mut Archetype,
change_tick: u32,
current_tick: u32,
) -> Option<Self::State> {
let ptr = archetype as *mut Archetype;
let state_a = A::prepare(unsafe { &mut *ptr }, change_tick, current_tick)?;
let state_b = B::prepare(unsafe { &mut *ptr }, change_tick, current_tick)?;
let state_c = C::prepare(unsafe { &mut *ptr }, change_tick, current_tick)?;
Some((state_a, state_b, state_c))
}
unsafe fn fetch(state: &mut Self::State, row: usize) -> Option<Self::Item> {
Some((
A::fetch(&mut state.0, row)?,
B::fetch(&mut state.1, row)?,
C::fetch(&mut state.2, row)?,
))
}
}
unsafe impl<
'w,
A: QueryFetchMut<'w>,
B: QueryFetchMut<'w>,
C: QueryFetchMut<'w>,
D: QueryFetchMut<'w>,
> QueryFetchMut<'w> for (A, B, C, D)
where
A: QueryFilter,
B: QueryFilter,
C: QueryFilter,
D: QueryFilter,
{
type Item = (A::Item, B::Item, C::Item, D::Item);
type State = (A::State, B::State, C::State, D::State);
fn prepare(
archetype: &'w mut Archetype,
change_tick: u32,
current_tick: u32,
) -> Option<Self::State> {
let ptr = archetype as *mut Archetype;
let state_a = A::prepare(unsafe { &mut *ptr }, change_tick, current_tick)?;
let state_b = B::prepare(unsafe { &mut *ptr }, change_tick, current_tick)?;
let state_c = C::prepare(unsafe { &mut *ptr }, change_tick, current_tick)?;
let state_d = D::prepare(unsafe { &mut *ptr }, change_tick, current_tick)?;
Some((state_a, state_b, state_c, state_d))
}
unsafe fn fetch(state: &mut Self::State, row: usize) -> Option<Self::Item> {
Some((
A::fetch(&mut state.0, row)?,
B::fetch(&mut state.1, row)?,
C::fetch(&mut state.2, row)?,
D::fetch(&mut state.3, row)?,
))
}
}
unsafe impl<'w, A: QueryFetch<'w>> QueryFetch<'w> for (A,) {
type Item = (A::Item,);
type State = (A::State,);
fn prepare(archetype: &'w Archetype, change_tick: u32) -> Option<Self::State> {
Some((A::prepare(archetype, change_tick)?,))
}
unsafe fn fetch(state: &Self::State, row: usize) -> Option<Self::Item> {
Some((A::fetch(&state.0, row)?,))
}
}
unsafe impl<'w, A: QueryFetch<'w>, B: QueryFetch<'w>> QueryFetch<'w> for (A, B) {
type Item = (A::Item, B::Item);
type State = (A::State, B::State);
fn prepare(archetype: &'w Archetype, change_tick: u32) -> Option<Self::State> {
Some((
A::prepare(archetype, change_tick)?,
B::prepare(archetype, change_tick)?,
))
}
unsafe fn fetch(state: &Self::State, row: usize) -> Option<Self::Item> {
Some((A::fetch(&state.0, row)?, B::fetch(&state.1, row)?))
}
}
unsafe impl<'w, A: QueryFetch<'w>, B: QueryFetch<'w>, C: QueryFetch<'w>> QueryFetch<'w>
for (A, B, C)
{
type Item = (A::Item, B::Item, C::Item);
type State = (A::State, B::State, C::State);
fn prepare(archetype: &'w Archetype, change_tick: u32) -> Option<Self::State> {
Some((
A::prepare(archetype, change_tick)?,
B::prepare(archetype, change_tick)?,
C::prepare(archetype, change_tick)?,
))
}
unsafe fn fetch(state: &Self::State, row: usize) -> Option<Self::Item> {
Some((
A::fetch(&state.0, row)?,
B::fetch(&state.1, row)?,
C::fetch(&state.2, row)?,
))
}
}
unsafe impl<'w, A: QueryFetch<'w>, B: QueryFetch<'w>, C: QueryFetch<'w>, D: QueryFetch<'w>>
QueryFetch<'w> for (A, B, C, D)
{
type Item = (A::Item, B::Item, C::Item, D::Item);
type State = (A::State, B::State, C::State, D::State);
fn prepare(archetype: &'w Archetype, change_tick: u32) -> Option<Self::State> {
Some((
A::prepare(archetype, change_tick)?,
B::prepare(archetype, change_tick)?,
C::prepare(archetype, change_tick)?,
D::prepare(archetype, change_tick)?,
))
}
unsafe fn fetch(state: &Self::State, row: usize) -> Option<Self::Item> {
Some((
A::fetch(&state.0, row)?,
B::fetch(&state.1, row)?,
C::fetch(&state.2, row)?,
D::fetch(&state.3, row)?,
))
}
}
pub struct QueryState<F> {
matches: Vec<usize>,
seen_archetypes: usize,
phantom: PhantomData<F>,
#[cfg(feature = "profiling")]
pub profiling_data: QueryProfilingData,
}
impl<F: QueryFilter> QueryState<F> {
pub fn new(world: &World) -> Self {
#[cfg(feature = "profiling")]
let span = info_span!("query_state.new", archetype_count = world.archetype_count());
#[cfg(feature = "profiling")]
let start = std::time::Instant::now();
let matched = world.get_cached_query_indices::<F>();
#[cfg(feature = "profiling")]
let prepare_time_us = start.elapsed().as_micros() as u64;
Self {
matches: matched.to_vec(),
seen_archetypes: world.archetypes().len(),
phantom: PhantomData,
#[cfg(feature = "profiling")]
profiling_data: QueryProfilingData {
prepare_time_us,
..Default::default()
},
}
}
pub fn iter<'w>(&self, world: &'w World, change_tick: u32) -> QueryIter<'w, F>
where
F: QueryFetch<'w>,
{
#[cfg(feature = "profiling")]
let _span = info_span!("QueryState::iter", matches = ?self.matches.len()).entered();
QueryIter::new(world, &self.matches, change_tick)
}
#[cfg(feature = "profiling")]
pub fn profiling_summary(&self) -> String {
format!(
"Query profiling: prep={:.2}μs, iter={:.2}μs, iterations={}",
self.profiling_data.prepare_time_us as f64,
self.profiling_data.avg_iteration_time_us,
self.profiling_data.total_iterations
)
}
pub fn iter_mut<'w>(&'w mut self, world: &'w mut World, change_tick: u32) -> QueryIterMut<'w, F>
where
F: QueryFetchMut<'w>,
{
QueryIterMut::new(world, &self.matches, change_tick, world.tick())
}
pub fn match_count(&self) -> usize {
self.matches.len()
}
pub fn update(&mut self, world: &World) {
#[cfg(feature = "profiling")]
let _span = info_span!("query_state.update").entered();
let count = world.archetype_count();
if count > self.seen_archetypes {
for (id, arch) in world
.archetypes()
.iter()
.enumerate()
.skip(self.seen_archetypes)
{
if F::matches_archetype(arch) {
self.matches.push(id);
}
}
self.seen_archetypes = count;
}
}
pub fn iter_simd_chunks<T: Component + Copy>(&mut self, world: &mut World) -> Vec<&mut [T]> {
let mut chunks = Vec::new();
self.update(world);
for &archetype_id in &self.matches {
if let Some(archetype) = world.get_archetype_mut(archetype_id) {
if let Some(col) = archetype.get_column_mut(std::any::TypeId::of::<T>()) {
let len = col.len();
let ptr = col.get_ptr_mut(0) as *mut T;
unsafe {
let slice = std::slice::from_raw_parts_mut(ptr, len);
chunks.extend(crate::simd::chunks(slice));
}
}
}
}
chunks
}
}
pub struct Query<'w, Q>
where
Q: QueryFilter + QueryFetch<'w>,
{
world: &'w World,
_phantom: PhantomData<Q>,
}
impl<'w, Q> Query<'w, Q>
where
Q: QueryFilter + QueryFetch<'w>,
{
pub fn new(world: &'w World) -> Self {
Self {
world,
_phantom: PhantomData,
}
}
pub fn iter(&self) -> QueryIterOwned<'w, Q> {
let matched = self.world.get_cached_query_indices::<Q>();
QueryIterOwned {
world: self.world,
matches: matched,
archetype_index: 0,
entity_index: 0,
change_tick: 0, state: None,
_phantom: PhantomData,
}
}
pub fn count(&self) -> usize {
let matched = self.world.get_cached_query_indices::<Q>();
matched
.iter()
.filter_map(|&id| self.world.get_archetype(id))
.map(|arch| arch.len())
.sum()
}
}
pub struct QueryIterOwned<'w, Q: QueryFilter>
where
Q: QueryFetch<'w>,
{
world: &'w World,
matches: Vec<usize>,
archetype_index: usize,
entity_index: usize,
change_tick: u32,
state: Option<Q::State>,
_phantom: PhantomData<Q>,
}
impl<'w, Q> Iterator for QueryIterOwned<'w, Q>
where
Q: QueryFilter + QueryFetch<'w>,
{
type Item = <Q as QueryFetch<'w>>::Item;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.state.is_none() {
if self.archetype_index >= self.matches.len() {
return None;
}
let arch_id = self.matches[self.archetype_index];
let archetype = self.world.get_archetype(arch_id)?;
self.state = Q::prepare(archetype, self.change_tick);
self.entity_index = 0;
if self.state.is_none() {
self.archetype_index += 1;
continue;
}
}
let arch_id = self.matches[self.archetype_index];
let archetype = self.world.get_archetype(arch_id)?;
if self.entity_index < archetype.len() {
let row = self.entity_index;
self.entity_index += 1;
if let Some(item) = unsafe { Q::fetch(self.state.as_ref().unwrap(), row) } {
return Some(item);
} else {
continue;
}
} else {
self.state = None;
self.archetype_index += 1;
}
}
}
}
impl<'w, Q> ExactSizeIterator for QueryIterOwned<'w, Q>
where
Q: QueryFilter + QueryFetch<'w>,
{
fn len(&self) -> usize {
let mut count = 0;
for &arch_id in &self.matches {
if let Some(arch) = self.world.get_archetype(arch_id) {
count += arch.len();
}
}
count.saturating_sub(self.entity_index)
}
}
pub struct CachedQuery<F: QueryFilter> {
state: QueryState<F>,
last_run_tick: u32,
}
impl<F: QueryFilter> CachedQuery<F> {
pub fn new(world: &World) -> Self {
Self {
state: QueryState::new(world),
last_run_tick: 0,
}
}
pub fn iter<'w>(&mut self, world: &'w World) -> QueryIter<'w, F>
where
F: QueryFetch<'w>,
{
self.state.update(world);
let iter = self.state.iter(world, self.last_run_tick);
self.last_run_tick = world.tick();
iter
}
pub fn iter_mut<'w>(&'w mut self, world: &'w mut World) -> QueryIterMut<'w, F>
where
F: QueryFetchMut<'w>,
{
let tick = world.tick();
let iter = self.state.iter_mut(world, self.last_run_tick);
self.last_run_tick = tick;
iter
}
}
pub struct With<T>(PhantomData<T>);
impl<T: 'static> QueryFilter for With<T> {
fn matches_archetype(archetype: &Archetype) -> bool {
archetype.signature().contains(&TypeId::of::<T>())
}
fn type_ids() -> SmallVec<[TypeId; MAX_FILTER_COMPONENTS]> {
smallvec![TypeId::of::<T>()]
}
}
unsafe impl<'w, T: 'static> QueryFetch<'w> for With<T> {
type Item = ();
type State = ();
fn prepare(_archetype: &'w Archetype, _change_tick: u32) -> Option<Self::State> {
Some(())
}
unsafe fn fetch(_state: &Self::State, _row: usize) -> Option<Self::Item> {
Some(())
}
}
unsafe impl<'w, T: 'static> QueryFetchMut<'w> for With<T> {
type Item = ();
type State = ();
fn prepare(
_archetype: &'w mut Archetype,
_change_tick: u32,
_current_tick: u32,
) -> Option<Self::State> {
Some(())
}
unsafe fn fetch(_state: &mut Self::State, _row: usize) -> Option<Self::Item> {
Some(())
}
}
pub struct Without<T>(PhantomData<T>);
impl<T: 'static> QueryFilter for Without<T> {
fn matches_archetype(archetype: &Archetype) -> bool {
!archetype.signature().contains(&TypeId::of::<T>())
}
fn type_ids() -> SmallVec<[TypeId; MAX_FILTER_COMPONENTS]> {
smallvec![] }
fn signature() -> QuerySignature {
let mut sig = QuerySignature::new();
sig.excluded.push(TypeId::of::<T>());
sig
}
}
unsafe impl<'w, T: 'static> QueryFetch<'w> for Without<T> {
type Item = ();
type State = ();
fn prepare(_archetype: &'w Archetype, _change_tick: u32) -> Option<Self::State> {
Some(())
}
unsafe fn fetch(_state: &Self::State, _row: usize) -> Option<Self::Item> {
Some(())
}
}
unsafe impl<'w, T: 'static> QueryFetchMut<'w> for Without<T> {
type Item = ();
type State = ();
fn prepare(
_archetype: &'w mut Archetype,
_change_tick: u32,
_current_tick: u32,
) -> Option<Self::State> {
Some(())
}
unsafe fn fetch(_state: &mut Self::State, _row: usize) -> Option<Self::Item> {
Some(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Entity;
impl QueryFilter for Entity {
fn matches_archetype(_archetype: &Archetype) -> bool {
true }
fn type_ids() -> SmallVec<[TypeId; MAX_FILTER_COMPONENTS]> {
smallvec![] }
}
unsafe impl<'w> QueryFetch<'w> for Entity {
type Item = EntityId;
type State = &'w [EntityId];
fn prepare(archetype: &'w Archetype, _change_tick: u32) -> Option<Self::State> {
Some(archetype.entities())
}
unsafe fn fetch(state: &Self::State, row: usize) -> Option<Self::Item> {
state.get(row).copied()
}
}
unsafe impl<'w> QueryFetchMut<'w> for Entity {
type Item = EntityId;
type State = *const [EntityId];
fn prepare(
archetype: &'w mut Archetype,
_change_tick: u32,
_current_tick: u32,
) -> Option<Self::State> {
Some(archetype.entities() as *const [EntityId])
}
unsafe fn fetch(state: &mut Self::State, row: usize) -> Option<Self::Item> {
let slice = unsafe { &**state };
slice.get(row).copied()
}
}
pub struct Changed<T: Component>(PhantomData<T>);
impl<T: Component> QueryFilter for Changed<T> {
fn matches_archetype(archetype: &Archetype) -> bool {
archetype.column_index(TypeId::of::<T>()).is_some()
}
fn type_ids() -> SmallVec<[TypeId; MAX_FILTER_COMPONENTS]> {
smallvec![]
}
fn signature() -> QuerySignature {
let mut sig = QuerySignature::new();
sig.required.push(TypeId::of::<T>());
sig
}
}
macro_rules! impl_query_filter {
($($T:ident),*) => {
#[allow(non_snake_case)]
impl<$($T: QueryFilter),*> QueryFilter for ($($T,)*) {
fn matches_archetype(archetype: &Archetype) -> bool {
$($T::matches_archetype(archetype))&&*
}
fn type_ids() -> SmallVec<[TypeId; MAX_FILTER_COMPONENTS]> {
let mut ids = SmallVec::new();
$(ids.extend($T::type_ids());)*
ids
}
fn signature() -> QuerySignature {
let mut sig = QuerySignature::new();
$(
let child_sig = $T::signature();
sig.required.extend(child_sig.required);
sig.excluded.extend(child_sig.excluded);
)*
sig.required.sort();
sig.excluded.sort();
sig.required.dedup();
sig.excluded.dedup();
sig
}
}
};
}
impl_query_filter!(A);
impl_query_filter!(A, B);
impl_query_filter!(A, B, C);
impl_query_filter!(A, B, C, D);
impl_query_filter!(A, B, C, D, E);
impl_query_filter!(A, B, C, D, E, F);
impl_query_filter!(A, B, C, D, E, F, G);
impl_query_filter!(A, B, C, D, E, F, G, H);
unsafe impl<'w, T: Component> QueryFetch<'w> for Changed<T> {
type Item = ();
type State = (&'w [u32], u32);
fn prepare(archetype: &'w Archetype, change_tick: u32) -> Option<Self::State> {
let idx = archetype.column_index(TypeId::of::<T>())?;
let col = archetype.get_column_by_index(idx)?;
if !col.changed_since(change_tick) {
return None;
}
Some((&col.changed_ticks, change_tick))
}
unsafe fn fetch(state: &Self::State, row: usize) -> Option<Self::Item> {
if row < state.0.len() && state.0[row] > state.1 {
Some(())
} else {
None
}
}
}
unsafe impl<'w, T: Component> QueryFetchMut<'w> for Changed<T> {
type Item = ();
type State = (&'w [u32], u32);
fn prepare(
archetype: &'w mut Archetype,
change_tick: u32,
_current_tick: u32,
) -> Option<Self::State> {
<Changed<T> as QueryFetch>::prepare(archetype, change_tick)
}
unsafe fn fetch(state: &mut Self::State, row: usize) -> Option<Self::Item> {
<Changed<T> as QueryFetch>::fetch(state, row)
}
}
pub struct Added<T>(PhantomData<T>);
impl<T: Component> QueryFilter for Added<T> {
fn matches_archetype(archetype: &Archetype) -> bool {
archetype.signature().contains(&TypeId::of::<T>())
}
fn type_ids() -> SmallVec<[TypeId; MAX_FILTER_COMPONENTS]> {
smallvec![]
}
fn signature() -> QuerySignature {
let mut sig = QuerySignature::new();
sig.required.push(TypeId::of::<T>());
sig
}
}
unsafe impl<'w, T: Component> QueryFetch<'w> for Added<T> {
type Item = ();
type State = (&'w [u32], u32);
fn prepare(archetype: &'w Archetype, change_tick: u32) -> Option<Self::State> {
let idx = archetype.column_index(TypeId::of::<T>())?;
let col = archetype.get_column_by_index(idx)?;
if !col.added_since(change_tick) {
return None;
}
Some((&col.added_ticks, change_tick))
}
unsafe fn fetch(state: &Self::State, row: usize) -> Option<Self::Item> {
if row < state.0.len() && state.0[row] > state.1 {
Some(())
} else {
None
}
}
}
unsafe impl<'w, T: Component> QueryFetchMut<'w> for Added<T> {
type Item = ();
type State = (&'w [u32], u32);
fn prepare(
archetype: &'w mut Archetype,
change_tick: u32,
_current_tick: u32,
) -> Option<Self::State> {
<Added<T> as QueryFetch>::prepare(archetype, change_tick)
}
unsafe fn fetch(state: &mut Self::State, row: usize) -> Option<Self::Item> {
<Added<T> as QueryFetch>::fetch(state, row)
}
}
pub struct Read<T>(PhantomData<T>);
impl<T: 'static> QueryFilter for Read<T> {
fn matches_archetype(archetype: &Archetype) -> bool {
archetype.signature().contains(&TypeId::of::<T>())
}
fn type_ids() -> SmallVec<[TypeId; MAX_FILTER_COMPONENTS]> {
smallvec![TypeId::of::<T>()]
}
}
unsafe impl<'w, T: Component> QueryFetch<'w> for Read<T> {
type Item = &'w T;
type State = &'w ComponentColumn;
fn prepare(archetype: &'w Archetype, _change_tick: u32) -> Option<Self::State> {
let type_id = TypeId::of::<T>();
archetype.get_column(type_id)
}
unsafe fn fetch(state: &Self::State, row: usize) -> Option<Self::Item> {
state.get::<T>(row)
}
}
pub struct Write<T>(PhantomData<T>);
impl<T: 'static> QueryFilter for Write<T> {
fn matches_archetype(archetype: &Archetype) -> bool {
archetype.signature().contains(&TypeId::of::<T>())
}
fn type_ids() -> SmallVec<[TypeId; MAX_FILTER_COMPONENTS]> {
smallvec![TypeId::of::<T>()]
}
}
unsafe impl<'w, T: Component> QueryFetchMut<'w> for Write<T> {
type Item = &'w mut T;
type State = (*mut ComponentColumn, u32);
fn prepare(
archetype: &'w mut Archetype,
_change_tick: u32,
current_tick: u32,
) -> Option<Self::State> {
let type_id = TypeId::of::<T>();
archetype
.get_column_mut(type_id)
.map(|column| (column as *mut ComponentColumn, current_tick))
}
unsafe fn fetch(state: &mut Self::State, row: usize) -> Option<Self::Item> {
let (column_ptr, current_tick) = state;
let column = &mut **column_ptr;
column.set_changed_tick(row, *current_tick);
column.get_mut::<T>(row)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_query_state_creation() {
let world = crate::World::new();
let state = QueryState::<&i32>::new(&world);
assert_eq!(state.match_count(), 0);
}
#[test]
fn test_incremental_update() {
let mut world = crate::World::new();
let mut query = CachedQuery::<&i32>::new(&world);
let initial_count = query.state.match_count();
world.spawn_entity((10i32,));
let count = query.iter(&world).count();
assert_eq!(count, 1);
assert!(query.state.match_count() > initial_count);
}
#[test]
fn test_query_filters() {
let mut world = crate::World::new();
#[derive(Debug, Clone, Copy)]
struct A;
#[derive(Debug, Clone, Copy)]
struct B;
world.spawn_entity((A, B));
world.spawn_entity((A,));
world.spawn_entity((B,));
let mut query = CachedQuery::<(&A, With<B>)>::new(&world);
assert_eq!(query.iter(&world).count(), 1);
let mut query = CachedQuery::<(&A, Without<B>)>::new(&world);
assert_eq!(query.iter(&world).count(), 1);
}
#[test]
fn test_change_detection() {
let mut world = crate::World::new();
struct Data(#[allow(dead_code)] i32);
let _e = world.spawn_entity((Data(1),));
world.increment_tick();
{
let mut query = QueryMut::<(&Data, Changed<Data>)>::new(&mut world);
assert_eq!(query.iter_since(0).count(), 1);
}
{
let mut query = QueryMut::<(&Data, Changed<Data>)>::new(&mut world);
assert_eq!(query.iter_since(2).count(), 0);
}
world.increment_tick(); {
let mut query = QueryMut::<(&Data, Changed<Data>)>::new(&mut world);
for (_data, _) in query.iter() {
}
}
}
}
pub struct View<'w, Q: QueryFilter + QueryFetch<'w>> {
states: Vec<(usize, Q::State)>,
_phantom: PhantomData<&'w Q>,
}
impl<'w, Q: QueryFilter + QueryFetch<'w>> View<'w, Q> {
pub fn new(world: &'w World, change_tick: u32) -> Self {
let matched = world.get_cached_query_indices::<Q>();
let mut states = Vec::with_capacity(matched.len());
for &id in &matched {
if let Some(archetype) = world.get_archetype(id) {
if !archetype.is_empty() {
if let Some(state) = Q::prepare(archetype, change_tick) {
states.push((archetype.len(), state));
}
}
}
}
Self {
states,
_phantom: PhantomData,
}
}
pub fn iter<'v>(&'v self) -> ViewIter<'v, 'w, Q> {
ViewIter {
view: self,
chunk_index: 0,
entity_index: 0,
}
}
}
impl<'v, 'w, Q: QueryFilter + QueryFetch<'w>> IntoIterator for &'v View<'w, Q> {
type Item = Q::Item;
type IntoIter = ViewIter<'v, 'w, Q>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct ViewIter<'v, 'w, Q: QueryFilter + QueryFetch<'w>> {
view: &'v View<'w, Q>,
chunk_index: usize,
entity_index: usize,
}
impl<'v, 'w, Q: QueryFilter + QueryFetch<'w>> Iterator for ViewIter<'v, 'w, Q> {
type Item = Q::Item;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.chunk_index >= self.view.states.len() {
return None;
}
let (len, ref state) = self.view.states[self.chunk_index];
if self.entity_index >= len {
self.chunk_index += 1;
self.entity_index = 0;
continue;
}
let row = self.entity_index;
self.entity_index += 1;
if let Some(item) = unsafe { Q::fetch(state, row) } {
return Some(item);
}
}
}
}