use super::{
SolidPreviewState, live_update,
request::{PlanJob, PlannedFrame, RedrawRequest},
};
use std::sync::{
Arc, PoisonError,
atomic::Ordering,
mpsc::{self, Sender},
};
#[derive(Debug, Clone, Copy, Default)]
struct InstantClock;
impl live_update::Clock for InstantClock {
fn now_ms(&self) -> f64 {
static EPOCH: std::sync::OnceLock<std::time::Instant> = std::sync::OnceLock::new();
EPOCH
.get_or_init(std::time::Instant::now)
.elapsed()
.as_secs_f64()
* 1000.0
}
}
pub fn build_planned_frame(job: PlanJob, budget: std::time::Duration) -> PlannedFrame {
indicatrix_solid::preview::build_planned_frame(job, budget, &InstantClock)
}
impl SolidPreviewState {
pub(super) fn submit_plan(&self, job: PlanJob) {
let tx = {
let mut guard = self
.plan_wake
.lock()
.unwrap_or_else(PoisonError::into_inner);
if guard.is_none() {
*guard = Some(self.spawn_plan_worker());
}
guard
.clone()
.expect("just initialized above if it was empty")
};
if self.plan_gate.submit(job).is_some() {
let _ = tx.send(());
}
}
fn spawn_plan_worker(&self) -> Sender<()> {
let (tx, rx) = mpsc::channel::<()>();
let plan_gate = Arc::clone(&self.plan_gate);
let generation_floor = Arc::clone(&self.generation_floor);
let self_weak = self
.self_weak
.lock()
.unwrap_or_else(PoisonError::into_inner)
.clone();
std::thread::spawn(move || {
for () in rx {
let Some(job) = plan_gate.take() else {
continue;
};
if job.generation < generation_floor.load(Ordering::Relaxed) {
continue;
}
let frame = build_planned_frame(job, live_update::DEFAULT_PREVIEW_BUDGET);
if let Some(state) = self_weak.upgrade() {
state.submit(RedrawRequest::Planned(Box::new(frame)));
}
}
});
tx
}
}