use std::rc::Rc;
use sim_kernel::{Expr, Result};
use sim_value::build;
use crate::{DeviceProfile, EncodedScene, FrameClock, LocalAdapter};
#[derive(Clone, Debug, PartialEq)]
pub struct AdapterInput<S> {
pub encoded: EncodedScene,
pub encoded_seq: u64,
pub state: S,
pub state_seq: u64,
}
impl<S> AdapterInput<S> {
pub fn new(encoded: EncodedScene, encoded_seq: u64, state: S, state_seq: u64) -> Self {
Self {
encoded,
encoded_seq,
state,
state_seq,
}
}
pub fn from_shared_scene(scene: Rc<Expr>, encoded_seq: u64, state: S, state_seq: u64) -> Self {
Self::new(
EncodedScene::from_shared(scene),
encoded_seq,
state,
state_seq,
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StalePolicy {
HoldLast,
Predict,
Blank,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Frame {
pub out: Rc<Expr>,
pub dropped: u32,
pub stale: bool,
pub seq: u64,
pub encoded_seq: u64,
}
#[derive(Clone, Debug)]
pub struct AdapterLoop<A> {
adapter: A,
policy: StalePolicy,
last: Option<Rc<Expr>>,
pending: u32,
}
impl<A> AdapterLoop<A> {
pub fn new(adapter: A, policy: StalePolicy) -> Self {
Self {
adapter,
policy,
last: None,
pending: 0,
}
}
pub fn adapter(&self) -> &A {
&self.adapter
}
pub fn policy(&self) -> StalePolicy {
self.policy
}
pub fn pending(&self) -> u32 {
self.pending
}
}
impl<A: LocalAdapter> AdapterLoop<A> {
pub fn offer(&mut self, _state: &A::State) {
self.pending = self.pending.saturating_add(1);
}
pub fn step(
&mut self,
clock: &FrameClock,
input: &AdapterInput<A::State>,
profile: &DeviceProfile,
) -> Result<Frame> {
let dropped = self.pending.saturating_sub(1);
self.pending = 0;
let stale = clock.stale(input.state_seq);
let out = if stale {
match self.policy {
StalePolicy::HoldLast => {
self.last.clone().unwrap_or_else(|| input.encoded.shared())
}
StalePolicy::Predict => {
self.adapter.adapt(&input.encoded, &input.state, profile)?
}
StalePolicy::Blank => Rc::new(blank_frame(profile)),
}
} else {
self.adapter.adapt(&input.encoded, &input.state, profile)?
};
self.last = Some(Rc::clone(&out));
Ok(Frame {
out,
dropped,
stale,
seq: clock.tick,
encoded_seq: input.encoded_seq,
})
}
}
pub fn blank_frame(profile: &DeviceProfile) -> Expr {
build::map(vec![
("kind", build::qsym("device", "blank-frame")),
("tier", Expr::Symbol(profile.tier.to_symbol())),
])
}