sim_lib_view_device/
loop.rs1use std::rc::Rc;
4
5use sim_kernel::{Expr, Result};
6use sim_value::build;
7
8use crate::{DeviceProfile, EncodedScene, FrameClock, LocalAdapter};
9
10#[derive(Clone, Debug, PartialEq)]
12pub struct AdapterInput<S> {
13 pub encoded: EncodedScene,
15 pub encoded_seq: u64,
17 pub state: S,
19 pub state_seq: u64,
21}
22
23impl<S> AdapterInput<S> {
24 pub fn new(encoded: EncodedScene, encoded_seq: u64, state: S, state_seq: u64) -> Self {
26 Self {
27 encoded,
28 encoded_seq,
29 state,
30 state_seq,
31 }
32 }
33
34 pub fn from_shared_scene(scene: Rc<Expr>, encoded_seq: u64, state: S, state_seq: u64) -> Self {
36 Self::new(
37 EncodedScene::from_shared(scene),
38 encoded_seq,
39 state,
40 state_seq,
41 )
42 }
43}
44
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum StalePolicy {
48 HoldLast,
50 Predict,
52 Blank,
54}
55
56#[derive(Clone, Debug, PartialEq)]
58pub struct Frame {
59 pub out: Rc<Expr>,
61 pub dropped: u32,
63 pub stale: bool,
65 pub seq: u64,
67 pub encoded_seq: u64,
69}
70
71#[derive(Clone, Debug)]
76pub struct AdapterLoop<A> {
77 adapter: A,
78 policy: StalePolicy,
79 last: Option<Rc<Expr>>,
80 pending: u32,
81}
82
83impl<A> AdapterLoop<A> {
84 pub fn new(adapter: A, policy: StalePolicy) -> Self {
86 Self {
87 adapter,
88 policy,
89 last: None,
90 pending: 0,
91 }
92 }
93
94 pub fn adapter(&self) -> &A {
96 &self.adapter
97 }
98
99 pub fn policy(&self) -> StalePolicy {
101 self.policy
102 }
103
104 pub fn pending(&self) -> u32 {
106 self.pending
107 }
108}
109
110impl<A: LocalAdapter> AdapterLoop<A> {
111 pub fn offer(&mut self, _state: &A::State) {
113 self.pending = self.pending.saturating_add(1);
114 }
115
116 pub fn step(
118 &mut self,
119 clock: &FrameClock,
120 input: &AdapterInput<A::State>,
121 profile: &DeviceProfile,
122 ) -> Result<Frame> {
123 let dropped = self.pending.saturating_sub(1);
124 self.pending = 0;
125 let stale = clock.stale(input.state_seq);
126 let out = if stale {
127 match self.policy {
128 StalePolicy::HoldLast => {
129 self.last.clone().unwrap_or_else(|| input.encoded.shared())
130 }
131 StalePolicy::Predict => {
132 self.adapter.adapt(&input.encoded, &input.state, profile)?
133 }
134 StalePolicy::Blank => Rc::new(blank_frame(profile)),
135 }
136 } else {
137 self.adapter.adapt(&input.encoded, &input.state, profile)?
138 };
139 self.last = Some(Rc::clone(&out));
140 Ok(Frame {
141 out,
142 dropped,
143 stale,
144 seq: clock.tick,
145 encoded_seq: input.encoded_seq,
146 })
147 }
148}
149
150pub fn blank_frame(profile: &DeviceProfile) -> Expr {
152 build::map(vec![
153 ("kind", build::qsym("device", "blank-frame")),
154 ("tier", Expr::Symbol(profile.tier.to_symbol())),
155 ])
156}