2020-07-17 02:20:51 +00:00
|
|
|
use crate::resource::Resources;
|
2020-07-14 23:23:25 +00:00
|
|
|
use fixedbitset::FixedBitSet;
|
2020-07-17 02:20:51 +00:00
|
|
|
use hecs::{Access, Query, World};
|
2020-07-16 00:20:36 +00:00
|
|
|
use std::{any::TypeId, borrow::Cow, collections::HashSet};
|
2020-07-10 04:18:35 +00:00
|
|
|
|
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub enum ThreadLocalExecution {
|
|
|
|
Immediate,
|
|
|
|
NextFlush,
|
2020-07-10 08:37:06 +00:00
|
|
|
}
|
2020-07-10 04:18:35 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
|
|
|
pub struct SystemId(pub u32);
|
|
|
|
|
|
|
|
impl SystemId {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
SystemId(rand::random::<u32>())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait System: Send + Sync {
|
|
|
|
fn name(&self) -> Cow<'static, str>;
|
|
|
|
fn id(&self) -> SystemId;
|
2020-07-14 02:29:34 +00:00
|
|
|
fn update_archetype_access(&mut self, world: &World);
|
2020-07-16 00:20:36 +00:00
|
|
|
fn archetype_access(&self) -> &ArchetypeAccess;
|
|
|
|
fn resource_access(&self) -> &TypeAccess;
|
2020-07-10 04:18:35 +00:00
|
|
|
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, _resources: &mut Resources) {}
|
|
|
|
}
|
2020-07-14 23:23:25 +00:00
|
|
|
|
|
|
|
// credit to Ratysz from the Yaks codebase
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct ArchetypeAccess {
|
|
|
|
pub immutable: FixedBitSet,
|
|
|
|
pub mutable: FixedBitSet,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ArchetypeAccess {
|
|
|
|
pub fn is_compatible(&self, other: &ArchetypeAccess) -> 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: &ArchetypeAccess) {
|
|
|
|
self.mutable.union_with(&other.mutable);
|
|
|
|
self.immutable.union_with(&other.immutable);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_access_for_query<Q>(&mut self, world: &World)
|
|
|
|
where
|
|
|
|
Q: Query,
|
|
|
|
{
|
|
|
|
let iterator = world.archetypes();
|
|
|
|
let bits = iterator.len();
|
|
|
|
self.immutable.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.immutable.set(archetype, true),
|
|
|
|
Access::Write => self.mutable.set(archetype, true),
|
|
|
|
Access::Iterate => (),
|
|
|
|
});
|
|
|
|
}
|
2020-07-15 02:05:39 +00:00
|
|
|
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.immutable.clear();
|
|
|
|
self.mutable.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-16 00:20:36 +00:00
|
|
|
#[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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-15 02:05:39 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2020-07-17 00:23:50 +00:00
|
|
|
use super::{ArchetypeAccess, TypeAccess};
|
2020-07-15 02:05:39 +00:00
|
|
|
use hecs::World;
|
2020-07-16 00:20:36 +00:00
|
|
|
use std::any::TypeId;
|
2020-07-17 02:20:51 +00:00
|
|
|
use crate::resource::{Res, ResourceQuery, ResMut, FetchResource};
|
2020-07-15 02:05:39 +00:00
|
|
|
|
|
|
|
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.immutable.contains(e1_archetype));
|
|
|
|
assert!(access.immutable.contains(e2_archetype));
|
|
|
|
assert!(access.immutable.contains(e3_archetype));
|
|
|
|
|
|
|
|
let mut access = ArchetypeAccess::default();
|
|
|
|
access.set_access_for_query::<(&A, &B)>(&world);
|
|
|
|
|
|
|
|
assert!(access.immutable.contains(e1_archetype) == false);
|
|
|
|
assert!(access.immutable.contains(e2_archetype));
|
|
|
|
assert!(access.immutable.contains(e3_archetype));
|
|
|
|
}
|
2020-07-16 00:20:36 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn resource_query_access() {
|
2020-07-17 00:23:50 +00:00
|
|
|
let access =
|
|
|
|
<<(Res<A>, ResMut<B>, Res<C>) as ResourceQuery>::Fetch as FetchResource>::access();
|
2020-07-16 00:20:36 +00:00
|
|
|
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);
|
|
|
|
}
|
2020-07-15 02:05:39 +00:00
|
|
|
}
|