Skip to main content

sva_engine/render/
drive.rs

1// Concern: pulls 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) -> the root's blocks
2
3use sva_samples::Extent;
4
5use super::RenderConfig;
6use super::table::spill::Spill;
7use super::table::{Pulled, Table};
8use super::until::{Known, Until};
9use crate::cache::Recording;
10use crate::error::EngineError;
11use crate::flops::Work;
12use crate::query::{DEFAULT_FRAME_SECS, Representation};
13
14pub(super) struct Driver {
15    pub(super) table: Table,
16    pub(super) start: i64,
17    pub(super) at: i64,
18    last: i64,
19    block: usize,
20    until: Option<Until>,
21    frame: usize,
22    stop: Option<i64>,
23    end: Option<i64>,
24    /// Root samples held behind `at`, for `until` to read its open frame.
25    keep: i64,
26    most_bytes: usize,
27    pub(super) work: Work,
28    pub(super) recording: Recording,
29    pub(super) spill: Option<Spill>,
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
59/// The `envelope` reading's own frame at the render's rate, where one is asked.
60pub(super) fn frame(config: &RenderConfig, rate: u32) -> usize {
61    let secs = config
62        .asks
63        .iter()
64        .find_map(|ask| match ask.representation {
65            Representation::Envelope { frame_secs } => Some(frame_secs),
66            _ => None,
67        })
68        .flatten()
69        .unwrap_or(DEFAULT_FRAME_SECS);
70    ((secs * f64::from(rate)).round() as usize).max(1)
71}
72
73impl Driver {
74    pub(super) fn new(
75        table: Table,
76        range: Extent,
77        block: usize,
78        config: &RenderConfig,
79        recording: Recording,
80    ) -> Driver {
81        let frame = frame(config, config.rate);
82        Driver {
83            table,
84            start: range.start,
85            at: range.start,
86            last: range.end,
87            block,
88            keep: config.until.as_ref().map_or(0, |_| frame as i64),
89            until: config.until.clone(),
90            frame,
91            stop: None,
92            end: None,
93            most_bytes: 0,
94            work: Work {
95                waves: Some(0),
96                ..Work::default()
97            },
98            recording,
99            spill: None,
100        }
101    }
102
103    pub(super) fn replace(&mut self, table: Table, last: i64) {
104        self.table = table;
105        self.last = last;
106        if self.stop.is_none() {
107            self.end = None;
108        }
109    }
110
111    pub(super) fn last(&self) -> i64 {
112        self.last
113    }
114
115    pub(super) fn next_to(&self) -> i64 {
116        self.last
117            .min(self.at.saturating_add(self.block as i64))
118            .max(self.at)
119    }
120
121    pub(super) fn next_block(&mut self) -> Result<Option<Block>, EngineError> {
122        let from = self.at;
123        if !self.pull()? {
124            return Ok(None);
125        }
126        let end = self.end.map_or(self.at, |end| end.clamp(from, self.at));
127        let held = self.table.samples(self.table.root, Extent::new(from, end));
128        Ok(Some(Block {
129            planes: held.planes,
130            start: from,
131        }))
132    }
133
134    pub(super) fn pull(&mut self) -> Result<bool, EngineError> {
135        let from = self.at;
136        if self.end.is_some_and(|end| from >= end) {
137            return Ok(false);
138        }
139        let to = self.next_to();
140        self.recording.reach(from);
141        let window = Extent::new(from, to);
142        if from == self.start {
143            let history = self
144                .table
145                .history(window, self.block as i64, &mut self.recording)?;
146            self.priced(&history);
147        }
148        let asked = self.spill.as_ref().map(|_| self.table.demand(window));
149        let pulled = self.table.pull(window, &mut self.recording)?;
150        self.priced(&pulled);
151        if let (Some(spill), Some(asked)) = (&mut self.spill, asked) {
152            spill.take(&self.table, &asked);
153        }
154        self.work.samples += (to - from) as u64;
155        self.at = to;
156        self.settle(from, to);
157        let future = (to < self.last).then(|| Extent::new(to, self.last));
158        let keep = Extent::new(from.min(to.saturating_sub(self.keep)), to);
159        self.table.release(future, keep, self.start);
160        Ok(true)
161    }
162
163    fn priced(&mut self, pulled: &Pulled) {
164        self.work.priced_flops += pulled.priced;
165        self.work.waves = self.work.waves.map(|held| held + pulled.waves);
166        self.most_bytes = self.most_bytes.max(pulled.most_bytes);
167    }
168
169    pub(super) fn most_bytes(&self) -> usize {
170        self.most_bytes
171    }
172
173    /// The one place `until` is checked. A level is known once its frame is whole, so only the
174    /// frames the last block left open are read again.
175    fn settle(&mut self, from: i64, to: i64) {
176        if self.end.is_some() {
177            return;
178        }
179        if self.last <= to {
180            self.end = Some(to);
181        }
182        let Some(until) = &self.until else {
183            return;
184        };
185        let frame = self.frame as i64;
186        let open = self.start + (from - 1 - self.start).div_euclid(frame) * frame;
187        let base = open.max(self.start);
188        let heard = self.table.samples(self.table.root, Extent::new(base, to));
189        let planes: Vec<&[f64]> = heard.planes.iter().map(Vec::as_slice).collect();
190        let rate = self.table.values[self.table.root].grid.rate;
191        let known = Known::new(planes, base, self.start, self.frame, rate, to == self.last);
192        if let Some(at) = until.first(&known, base, to) {
193            self.stop = Some(at);
194            self.end = Some(at.max(from));
195        }
196    }
197
198    pub(super) fn end(&self) -> Option<i64> {
199        self.end
200    }
201
202    pub(super) fn stop(&self) -> Option<i64> {
203        self.stop
204    }
205}