Skip to main content

sva_engine/render/
drive.rs

1// Concern: pulls or skips a table block by block until its range ends or `until` holds, dropping what no later block reads | Non-concern: what a value computes | IO: (Table, range) -> blocks
2
3use sva_samples::Extent;
4
5use super::RenderConfig;
6use super::table::{Pulled, Table};
7use super::until::{Known, Until};
8use crate::cache::Recording;
9use crate::error::EngineError;
10use crate::flops::Work;
11use crate::query::{DEFAULT_FRAME_SECS, Representation};
12
13pub(super) struct Driver {
14    pub(super) table: Table,
15    pub(super) start: i64,
16    pub(super) at: i64,
17    last: i64,
18    block: usize,
19    until: Option<Until>,
20    frame: usize,
21    stop: Option<i64>,
22    end: Option<i64>,
23    /// Root samples held behind `at`, for `until` to read its open frame.
24    keep: i64,
25    /// Where the samples heard without a break since start from: a skip starts them anew.
26    heard: i64,
27    most_bytes: usize,
28    pub(super) work: Work,
29    pub(super) recording: Recording,
30}
31
32pub struct Block {
33    planes: Vec<Vec<f64>>,
34    start: i64,
35}
36
37impl Block {
38    pub fn start(&self) -> i64 {
39        self.start
40    }
41
42    pub fn len(&self) -> usize {
43        self.planes[0].len()
44    }
45
46    pub fn is_empty(&self) -> bool {
47        self.len() == 0
48    }
49
50    pub fn width(&self) -> usize {
51        self.planes.len()
52    }
53
54    pub fn plane(&self, c: usize) -> &[f64] {
55        &self.planes[c]
56    }
57
58    /// A mono block in each of `width` channels; any other as it is.
59    pub(super) fn widened(mut self, width: usize) -> Block {
60        if self.planes.len() == 1 {
61            let copies = vec![self.planes[0].clone(); width.saturating_sub(1)];
62            self.planes.extend(copies);
63        }
64        self
65    }
66}
67
68/// The `envelope` reading's own frame at the render's rate, where one is asked.
69pub(super) fn frame(config: &RenderConfig, rate: u32) -> usize {
70    let secs = config
71        .asks
72        .iter()
73        .find_map(|ask| match ask.representation {
74            Representation::Envelope { frame_secs } => Some(frame_secs),
75            _ => None,
76        })
77        .flatten()
78        .unwrap_or(DEFAULT_FRAME_SECS);
79    ((secs * f64::from(rate)).round() as usize).max(1)
80}
81
82impl Driver {
83    pub(super) fn new(
84        table: Table,
85        range: Extent,
86        block: usize,
87        config: &RenderConfig,
88        recording: Recording,
89    ) -> Driver {
90        let frame = frame(config, config.rate);
91        Driver {
92            table,
93            start: range.start,
94            at: range.start,
95            heard: range.start,
96            last: range.end,
97            block,
98            keep: config.until.as_ref().map_or(0, |_| frame as i64),
99            until: config.until.clone(),
100            frame,
101            stop: None,
102            end: None,
103            most_bytes: 0,
104            work: Work {
105                waves: Some(0),
106                ..Work::default()
107            },
108            recording,
109        }
110    }
111
112    /// Ends at `last`, unless `until` already stopped it.
113    pub(super) fn bound(&mut self, last: i64) {
114        self.last = last;
115        if self.stop.is_none() {
116            self.end = None;
117        }
118    }
119
120    pub(super) fn last(&self) -> i64 {
121        self.last
122    }
123
124    pub(super) fn next(&self) -> Extent {
125        Extent::new(self.at, self.next_to(self.block))
126    }
127
128    fn next_to(&self, n: usize) -> i64 {
129        self.last.min(self.at.saturating_add(n as i64)).max(self.at)
130    }
131
132    /// `n` samples from where it stands, cut where its range ends; `None` from there on.
133    pub(super) fn read(&mut self, n: usize) -> Result<Option<Block>, EngineError> {
134        let from = self.at;
135        if !self.pulled(n)? {
136            return Ok(None);
137        }
138        let end = self.end.map_or(self.at, |end| end.clamp(from, self.at));
139        let held = self.table.samples(self.table.root, Extent::new(from, end));
140        Ok(Some(Block {
141            planes: held.planes,
142            start: from,
143        }))
144    }
145
146    /// Stands at `to`, computing nothing before it; the stateful values it started silent,
147    /// each asked from before its run reaches.
148    pub(super) fn skip(&mut self, to: i64) -> Result<Vec<usize>, EngineError> {
149        if self.end.is_some_and(|end| self.at >= end) {
150            return Ok(Vec::new());
151        }
152        let to = to.min(self.last);
153        let silenced = self.table.skipped(Extent::new(to, self.last))?;
154        self.recording.reach(to);
155        (self.at, self.heard) = (to, to);
156        let future = (to < self.last).then(|| Extent::new(to, self.last));
157        self.table.release(future, Extent::NOWHERE, self.start);
158        if self.last <= to {
159            self.end = Some(to);
160        }
161        Ok(silenced)
162    }
163
164    pub(super) fn pull(&mut self) -> Result<bool, EngineError> {
165        self.pulled(self.block)
166    }
167
168    /// Computes `n` samples on from where it stands; false once it ended.
169    pub(super) fn pulled(&mut self, n: usize) -> Result<bool, EngineError> {
170        let from = self.at;
171        if self.end.is_some_and(|end| from >= end) {
172            return Ok(false);
173        }
174        let to = self.next_to(n);
175        self.recording.reach(from);
176        let window = Extent::new(from, to);
177        if from == self.start {
178            let history = self
179                .table
180                .history(window, self.block as i64, &mut self.recording)?;
181            self.priced(&history);
182        }
183        let pulled = self.table.pull(window, &mut self.recording)?;
184        self.priced(&pulled);
185        self.work.samples += (to - from) as u64;
186        self.at = to;
187        self.settle(from, to);
188        let future = (to < self.last).then(|| Extent::new(to, self.last));
189        let keep = Extent::new(from.min(to.saturating_sub(self.keep)), to);
190        self.table.release(future, keep, self.start);
191        Ok(true)
192    }
193
194    fn priced(&mut self, pulled: &Pulled) {
195        self.work.priced_flops += pulled.priced;
196        self.work.waves = self.work.waves.map(|held| held + pulled.waves);
197        self.most_bytes = self.most_bytes.max(pulled.most_bytes);
198    }
199
200    pub(super) fn most_bytes(&self) -> usize {
201        self.most_bytes
202    }
203
204    /// The one place `until` is checked. A level is known once its frame is whole, so only the
205    /// frames the last block left open are read again; a frame a skip cut into is never known.
206    fn settle(&mut self, from: i64, to: i64) {
207        if self.end.is_some() {
208            return;
209        }
210        if self.last <= to {
211            self.end = Some(to);
212        }
213        let Some(until) = &self.until else {
214            return;
215        };
216        let frame = self.frame as i64;
217        let open = self.start + (from - 1 - self.start).div_euclid(frame) * frame;
218        let base = open.max(self.heard);
219        let heard = self.table.samples(self.table.root, Extent::new(base, to));
220        let planes: Vec<&[f64]> = heard.planes.iter().map(Vec::as_slice).collect();
221        let rate = self.table.values[self.table.root].grid.rate;
222        let known = Known::new(planes, base, self.start, self.frame, rate, to == self.last);
223        if let Some(at) = until.first(&known, base, to) {
224            self.stop = Some(at);
225            self.end = Some(at.max(from));
226        }
227    }
228
229    pub(super) fn end(&self) -> Option<i64> {
230        self.end
231    }
232
233    pub(super) fn stop(&self) -> Option<i64> {
234        self.stop
235    }
236}