use alloc::vec::Vec;
use super::BehaviorSystem;
use super::instance::Instance;
use crate::behavior::{Effect, Program, Val, View, exec};
use crate::components::Transform;
use crate::ecs::{ComponentStorage, Entity, EntityByName, PipelineContext};
pub(super) const PARALLEL_EVAL_MIN_JOBS: usize = 64;
#[derive(Debug, Default)]
pub struct EvalBucket {
pub(super) jobs: core::ops::Range<usize>,
pub(super) effects: Vec<Effect>,
pub(super) produced: Vec<(usize, Option<Entity>, usize)>,
pub(super) fired: Vec<(usize, Vec<u32>)>,
pub(super) bindings: Vec<Option<Val>>,
}
pub trait EvalScheduler: core::fmt::Debug + Send {
fn workers(&self) -> usize;
fn run(&self, buckets: &mut [EvalBucket], eval: &(dyn Fn(&mut EvalBucket) + Send + Sync));
}
#[derive(Debug, Default)]
pub(super) struct Snapshot {
pub(super) queries: Vec<Vec<Vec<Entity>>>,
pub(super) scoped: Vec<Vec<Entity>>,
}
pub(super) struct EvalCtx<'a> {
pub(super) components: &'a ComponentStorage,
pub(super) names: Option<&'a EntityByName>,
pub(super) snapshot: &'a Snapshot,
pub(super) programs: &'a [Program],
pub(super) instances: &'a [Vec<Instance>],
pub(super) vars: &'a [Val],
pub(super) dt: f32,
pub(super) elapsed: f32,
pub(super) tracing: bool,
}
pub(super) fn eval_one(
ec: &EvalCtx<'_>,
bindings: &mut Vec<Option<Val>>,
i: usize,
entity: Option<Entity>,
out: &mut Vec<Effect>,
) -> Option<(usize, Vec<u32>)> {
let locals = ec.instances[i]
.iter()
.find(|inst| inst.entity == entity)
.map(|inst| inst.locals.as_slice())?;
bindings.clear();
bindings.resize(ec.programs[i].bindings, None);
let mut nodes: Option<Vec<u32>> = ec.tracing.then(Vec::new);
let before = out.len();
let mut view = View {
dt: ec.dt,
elapsed: ec.elapsed,
vars: ec.vars,
locals,
bindings: bindings.as_mut_slice(),
queries: &ec.snapshot.queries[i],
by_name: &|id| {
ec.names
.and_then(|n| n.get(id))
.filter(|e| ec.components.is_alive(*e))
},
transforms: &|e| ec.components.get::<Transform>(e).copied(),
alive: &|e| ec.components.is_alive(e),
self_entity: entity,
trace: &mut nodes,
};
exec(&ec.programs[i].body, &mut view, out);
Some((out.len() - before, nodes.unwrap_or_default()))
}
impl BehaviorSystem {
pub(super) fn entities_matching_into(
ctx: &PipelineContext,
tags: &[u8],
scratch: &mut Vec<Entity>,
out: &mut Vec<Entity>,
) {
out.clear();
let Some((first, rest)) = tags.split_first() else {
return;
};
out.extend_from_slice(ctx.entities_with_tag(*first));
for tag in rest {
scratch.clear();
scratch.extend_from_slice(ctx.entities_with_tag(*tag));
scratch.sort_unstable_by_key(|e| e.to_bits());
out.retain(|e| {
scratch
.binary_search_by_key(&e.to_bits(), |o| o.to_bits())
.is_ok()
});
}
out.sort_unstable_by_key(|e| e.to_bits());
}
pub(super) fn gather(&mut self, ctx: &PipelineContext, snapshot: &mut Snapshot) {
snapshot.queries.resize_with(self.programs.len(), Vec::new);
snapshot.scoped.resize_with(self.programs.len(), Vec::new);
for (i, p) in self.programs.iter().enumerate() {
snapshot.queries[i].resize_with(p.queries.len(), Vec::new);
for (q, tags) in p.queries.iter().enumerate() {
Self::entities_matching_into(
ctx,
tags,
&mut self.tag_scratch,
&mut snapshot.queries[i][q],
);
}
Self::entities_matching_into(
ctx,
&p.scope,
&mut self.tag_scratch,
&mut snapshot.scoped[i],
);
}
}
}