use super::super::runner::{PathingFrame, SimulationRunner};
use super::super::step::{PathingStep, SimulationStepError};
use super::{SharedSimulationOutput, Simulation, SimulationControls};
use crate::effect::PathEffectParams;
use crate::ray_tracing::RayTracer;
use crate::simulation::{
DirectCompatible, Pathing, ReflectionEffectCompatible, ReflectionsCompatible,
SimulationFlagsProvider,
};
use arc_swap::ArcSwap;
use object_pool::Pool;
use std::collections::HashMap;
use std::hash::Hash;
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Condvar, Mutex};
impl<SourceId, T, D, R, RE> Simulation<SourceId, T, D, R, Pathing, RE>
where
SourceId: 'static + Send + Sync + Clone + Hash + Eq,
T: 'static + RayTracer,
D: 'static + Send + Sync + Clone + Default + DirectCompatible<D> + SimulationFlagsProvider,
R: 'static + Send + Sync + Clone + Default + ReflectionsCompatible<R> + SimulationFlagsProvider,
RE: 'static + Send + Sync + Clone + Default + ReflectionEffectCompatible<R, RE>,
(): DirectCompatible<D> + ReflectionsCompatible<R>,
{
pub fn spawn_pathing(
&mut self,
on_error: impl Fn(SimulationStepError) + Send + 'static,
) -> PathingSimulation<SourceId, D, R, RE> {
let input = Arc::new(ArcSwap::new(Arc::new(PathingFrame {
sources: self.sources.clone(),
shared_inputs: Default::default(),
})));
let output = SharedSimulationOutput(Arc::new(ArcSwap::new(Arc::new(
Arc::new(Pool::new(1, HashMap::default)).pull_owned(HashMap::default),
))));
let paused = Arc::new((Mutex::new(false), Condvar::new()));
let shutdown = Arc::new(AtomicBool::new(false));
self.shutdowns.push(shutdown.clone());
self.paused.push(paused.clone());
let simulator_for_commit = self.simulator.clone();
let handle = SimulationRunner::new(
input.clone(),
output.0.clone(),
self.simulator_commit_needed.clone(),
move || simulator_for_commit.commit(),
self.pending_scene_commits.clone(),
shutdown.clone(),
paused.clone(),
)
.spawn(
PathingStep::new::<SourceId>(self.simulator.clone()),
on_error,
);
PathingSimulation {
input,
output,
controls: SimulationControls::new(handle, paused, shutdown),
}
}
}
pub struct PathingSimulation<SourceId, D, R, RE>
where
SourceId: 'static + Send + Sync,
RE: ReflectionEffectCompatible<R, RE>,
{
input: SharedPathingInput<SourceId, D, R, RE>,
output: SharedSimulationOutput<HashMap<SourceId, PathEffectParams>>,
controls: SimulationControls,
}
impl<SourceId, D, R, RE> PathingSimulation<SourceId, D, R, RE>
where
SourceId: 'static + Send + Sync,
RE: ReflectionEffectCompatible<R, RE>,
{
pub fn set_input(&self, frame: PathingFrame<SourceId, D, R, Pathing, RE>) {
self.input.store(Arc::new(frame));
}
pub fn output(&self) -> SharedSimulationOutput<HashMap<SourceId, PathEffectParams>> {
self.output.clone()
}
pub fn pause(&self) {
self.controls.pause();
}
pub fn resume(&self) {
self.controls.resume();
}
pub fn shutdown(&self) {
self.controls.shutdown();
}
pub fn join(&mut self) -> std::thread::Result<()> {
self.controls.join()
}
}
type SharedPathingInput<SourceId, D, R, RE> =
Arc<ArcSwap<PathingFrame<SourceId, D, R, Pathing, RE>>>;
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
fn simulation() -> Simulation<(), DefaultRayTracer, (), (), Pathing, ()> {
let context = Context::default();
let audio_settings = AudioSettings::default();
let simulation_settings =
SimulationSettings::new(&audio_settings).with_pathing(PathingSimulationSettings {
num_visibility_samples: 4,
});
let mut simulator = Simulator::try_new(&context, &simulation_settings).unwrap();
let mut scene = Scene::try_new(&context).unwrap();
let vertices = vec![
Point::new(-10.0, 0.0, -10.0),
Point::new(10.0, 0.0, -10.0),
Point::new(10.0, 0.0, 10.0),
Point::new(-10.0, 0.0, 10.0),
];
let triangles = vec![Triangle::new(0, 1, 2), Triangle::new(0, 2, 3)];
let materials = vec![Material::default()];
let material_indices = vec![0usize, 0];
let static_mesh = StaticMesh::try_new(
&scene,
&StaticMeshSettings {
vertices: &vertices,
triangles: &triangles,
material_indices: &material_indices,
materials: &materials,
},
)
.unwrap();
scene.add_static_mesh(static_mesh);
scene.commit();
simulator.set_scene(&scene);
let mut probe_array = ProbeArray::try_new(&context).unwrap();
probe_array.generate_probes(
&scene,
&ProbeGenerationParams::UniformFloor {
spacing: 5.0,
height: 1.5,
transform: Matrix4::new([
[20.0, 0.0, 0.0, 0.0],
[0.0, 20.0, 0.0, 0.0],
[0.0, 0.0, 20.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]),
},
);
let mut probe_batch = ProbeBatch::try_new(&context).unwrap();
probe_batch.add_probe_array(&probe_array);
probe_batch.commit();
simulator.add_probe_batch(&probe_batch);
simulator.commit();
Simulation::new::<()>(simulator)
}
#[test]
fn test_spawn_and_shutdown() {
let mut simulation = simulation();
let mut pathing_simulation = simulation.spawn_pathing(|error| {
eprintln!("{error}");
});
simulation.shutdown();
pathing_simulation
.join()
.expect("simulation thread panicked");
}
#[test]
fn test_initial_output_is_empty() {
let mut simulation = simulation();
let mut pathing_simulation = simulation.spawn_pathing(|error| {
eprintln!("{error}");
});
assert!(pathing_simulation.output().load().is_empty());
simulation.shutdown();
pathing_simulation
.join()
.expect("simulation thread panicked");
}
}