use super::simulation::SourceWithInputs;
use super::step::SimulationStep;
use crate::geometry::Scene;
use crate::ray_tracing::RayTracer;
use arc_swap::ArcSwap;
use object_pool::{Pool, ReusableOwned};
use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::sync::{
Arc, Condvar, Mutex,
atomic::{AtomicBool, Ordering},
};
mod direct;
pub use direct::*;
mod pathing;
pub use pathing::*;
mod reflections;
pub use reflections::*;
mod reflections_reverb;
pub use reflections_reverb::*;
pub struct SimulationRunner<I, O, T: RayTracer> {
input: Arc<ArcSwap<I>>,
output: Arc<ArcSwap<ReusableOwned<O>>>,
simulator_commit_needed: Arc<AtomicBool>,
on_simulator_commit: Box<dyn FnMut() + Send + 'static>,
pending_scene_commits: Arc<ArcSwap<HashSet<Scene<T>>>>,
shutdown: Arc<AtomicBool>,
paused: Arc<(Mutex<bool>, Condvar)>,
}
impl<I, O, T> SimulationRunner<I, O, T>
where
I: Send + Sync + 'static,
O: Send + Sync + 'static + Default + Clear + Shrink,
T: RayTracer + 'static,
{
pub fn new(
input: Arc<ArcSwap<I>>,
output: Arc<ArcSwap<ReusableOwned<O>>>,
simulator_commit_needed: Arc<AtomicBool>,
on_simulator_commit: impl FnMut() + Send + 'static,
pending_scene_commits: Arc<ArcSwap<HashSet<Scene<T>>>>,
shutdown: Arc<AtomicBool>,
paused: Arc<(Mutex<bool>, Condvar)>,
) -> Self {
Self {
input,
output,
simulator_commit_needed,
on_simulator_commit: Box::new(on_simulator_commit),
pending_scene_commits,
shutdown,
paused,
}
}
pub fn spawn<S, E>(
self,
mut step: S,
on_error: impl Fn(E) + Send + 'static,
) -> std::thread::JoinHandle<()>
where
I: Resolve,
for<'a> S: SimulationStep<<I as Resolve>::Resolved<'a>, Output = O, Error = E>,
S: Send + 'static,
for<'a> O: Allocate<<I as Resolve>::Resolved<'a>>,
E: Send + 'static,
{
let Self {
input,
output,
simulator_commit_needed,
mut on_simulator_commit,
pending_scene_commits,
shutdown,
paused,
} = self;
std::thread::spawn(move || {
let pool = Arc::new(Pool::new(4, O::default));
let empty_scene_commits = Arc::new(HashSet::new());
loop {
if shutdown.load(Ordering::Relaxed) {
break;
}
{
let (lock, condvar) = &*paused;
let mut is_paused = lock.lock().unwrap();
while *is_paused {
is_paused = condvar.wait(is_paused).unwrap();
}
}
let scenes_to_commit = pending_scene_commits.swap(Arc::clone(&empty_scene_commits));
for scene in scenes_to_commit.iter() {
scene.commit();
}
if simulator_commit_needed
.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
{
on_simulator_commit();
}
let frame = input.load();
let resolved = frame.resolve();
let mut out = pool.pull_owned(|| O::allocate(&resolved));
out.clear();
match step.run(&resolved, &mut out) {
Ok(()) => {
out.shrink();
output.store(Arc::new(out));
}
Err(error) => on_error(error),
}
}
})
}
}
pub trait Clear {
fn clear(&mut self);
}
impl<T> Clear for Vec<T> {
fn clear(&mut self) {
Vec::clear(self);
}
}
impl<K, V> Clear for HashMap<K, V> {
fn clear(&mut self) {
HashMap::clear(self);
}
}
pub trait Shrink {
fn shrink(&mut self);
}
impl<T> Shrink for Vec<T> {
fn shrink(&mut self) {
if self.capacity() > self.len() * 3 {
self.shrink_to_fit();
}
}
}
impl<K, V> Shrink for HashMap<K, V>
where
K: Eq + Hash,
{
fn shrink(&mut self) {
if self.capacity() > self.len() * 2 {
self.shrink_to_fit();
}
}
}
pub trait Allocate<Input> {
fn allocate(input: &Input) -> Self;
}
pub trait Resolve {
type Resolved<'a>
where
Self: 'a;
fn resolve(&self) -> Self::Resolved<'_>;
}
pub(crate) type SourcesGuard<SourceId, D, R, P, RE> =
arc_swap::Guard<Arc<ReusableOwned<Vec<(SourceId, SourceWithInputs<D, R, P, RE>)>>>>;