1pub mod ops;
4pub mod renderer;
5
6use crate::buffer::Buffer;
7use crate::error::SampleError;
8use crate::filters::FilterSite;
9use crate::physics::{Solver, site};
10use ops::{Layout, Op, lower};
11use renderer::{NodeRenderer, Site};
12
13pub use ops::Layout as MachineLayout;
14
15struct Program {
17 ops: Vec<Op>,
18 widths: Vec<usize>,
19 sites: Vec<Site>,
20 pub width: usize,
21}
22
23pub struct Ctx<'a> {
27 pub rate: u32,
28 pub origin_secs: f64,
29 pub len: usize,
30 pub reads: &'a [&'a Buffer],
31 pub self_planes: &'a [f64],
32 pub written: usize,
33}
34
35impl NodeRenderer {
36 fn compile(&self, layout: &Layout) -> Result<Program, SampleError> {
37 let (mut ops, mut widths) = (Vec::new(), Vec::new());
38 let width = lower(self, layout, &mut ops, &mut widths)?;
39 Ok(Program {
40 ops,
41 widths,
42 sites: layout.sites.clone(),
43 width,
44 })
45 }
46
47 pub fn run(&self, layout: &Layout, ctx: &Ctx) -> Result<Buffer, SampleError> {
49 run(&self.compile(layout)?, ctx)
50 }
51}
52
53enum State {
54 Filter(FilterSite),
55 Physics(Box<dyn Solver>),
56}
57
58fn open(p: &Program, rate: u32) -> Result<Vec<State>, SampleError> {
61 let mut lanes = vec![1usize; p.sites.len()];
62 for (slot, op) in p.ops.iter().enumerate() {
63 if let Op::Filter(id) = op {
64 lanes[id.0 as usize] = p.widths[slot];
65 }
66 }
67 p.sites
68 .iter()
69 .zip(lanes)
70 .map(|(s, width)| {
71 Ok(match s {
72 Site::Filter(shape) => State::Filter(FilterSite::new(
73 *shape,
74 width,
75 &[0.0],
76 &[0.0],
77 &[0.0],
78 f64::from(rate),
79 )),
80 Site::Physics(params) => State::Physics(site(params, rate)?),
81 })
82 })
83 .collect()
84}
85
86struct Stack {
88 values: Vec<Vec<f64>>,
89 pending: Vec<usize>,
90}
91
92impl Stack {
93 fn of(widths: &[usize]) -> Stack {
94 Stack {
95 values: widths.iter().map(|&w| vec![0.0; w]).collect(),
96 pending: Vec::with_capacity(widths.len()),
97 }
98 }
99}
100
101fn part(v: &[f64], c: usize) -> f64 {
103 v[c % v.len()]
104}
105
106fn run(p: &Program, ctx: &Ctx) -> Result<Buffer, SampleError> {
107 let mut states = open(p, ctx.rate)?;
108 let mut out = Buffer::silence(ctx.rate, p.width, ctx.len);
109 out.origin_secs = ctx.origin_secs;
110 let mut stack = Stack::of(&p.widths);
111 let sr = f64::from(ctx.rate);
112 let mut own = vec![0.0; p.width * ctx.len];
113 for i in 0..ctx.len {
114 {
115 let held = Ctx {
116 rate: ctx.rate,
117 origin_secs: ctx.origin_secs,
118 len: ctx.len,
119 reads: ctx.reads,
120 self_planes: match ctx.self_planes.is_empty() {
121 true => &own,
122 false => ctx.self_planes,
123 },
124 written: match ctx.self_planes.is_empty() {
125 true => i,
126 false => ctx.written,
127 },
128 };
129 step(p, &held, &mut states, &mut stack, i, sr);
130 }
131 let top = &stack.values[*stack.pending.last().expect("a renderer leaves one value")];
132 for c in 0..p.width {
133 let value = part(top, c);
134 out.planes[c][i] = value;
135 own[c * ctx.len + i] = value;
136 }
137 }
138 Ok(out)
139}
140
141fn step(p: &Program, ctx: &Ctx, states: &mut [State], stack: &mut Stack, i: usize, sr: f64) {
144 let t = ctx.origin_secs + i as f64 / sr;
145 stack.pending.clear();
146 for (slot, op) in p.ops.iter().enumerate() {
147 let at = stack.pending.len() - arity_of(op);
148 let (done, rest) = stack.values.split_at_mut(slot);
149 fill(
150 op,
151 done,
152 &stack.pending[at..],
153 &mut rest[0],
154 ctx,
155 states,
156 i,
157 t,
158 sr,
159 );
160 stack.pending.truncate(at);
161 stack.pending.push(slot);
162 }
163}
164
165fn arity_of(op: &Op) -> usize {
166 match op {
167 Op::Const(_) | Op::Time | Op::Read { .. } | Op::SelfAt { .. } | Op::Physics(_) => 0,
168 Op::Map(_) | Op::Crop { .. } | Op::Channel(_) => 1,
169 Op::Sub | Op::Div | Op::Pow | Op::Zip(_) => 2,
170 Op::Add(n) | Op::Mul(n) | Op::Join(n) => *n,
171 Op::Filter(_) => 4,
172 }
173}
174
175#[allow(clippy::too_many_arguments)]
176fn fill(
177 op: &Op,
178 done: &[Vec<f64>],
179 srcs: &[usize],
180 result: &mut [f64],
181 ctx: &Ctx,
182 states: &mut [State],
183 i: usize,
184 t: f64,
185 sr: f64,
186) {
187 let arg = |k: usize| done[srcs[k]].as_slice();
188 match op {
189 Op::Const(v) => result[0] = *v,
190 Op::Time => result[0] = t,
191 Op::Read { id, shift } => {
192 let buffer = ctx.reads[id.0 as usize];
193 let at = i as i64 + shift;
194 for (c, slot) in result.iter_mut().enumerate() {
195 *slot = usize::try_from(at)
196 .ok()
197 .and_then(|k| buffer.plane(c).get(k).copied())
198 .unwrap_or(0.0);
199 }
200 }
201 Op::SelfAt { steps } => {
202 let at = i as i64 - i64::from(*steps);
203 for (c, slot) in result.iter_mut().enumerate() {
204 *slot = usize::try_from(at)
205 .ok()
206 .filter(|k| *k < ctx.written)
207 .and_then(|k| ctx.self_planes.get(c * ctx.len + k).copied())
208 .unwrap_or(0.0);
209 }
210 }
211 Op::Add(_) | Op::Mul(_) => {
212 let product = matches!(op, Op::Mul(_));
213 for (c, slot) in result.iter_mut().enumerate() {
214 *slot =
215 srcs.iter()
216 .enumerate()
217 .fold(f64::from(u8::from(product)), |acc, (k, _)| {
218 if product {
219 acc * part(arg(k), c)
220 } else {
221 acc + part(arg(k), c)
222 }
223 });
224 }
225 }
226 Op::Sub | Op::Div | Op::Pow | Op::Zip(_) => {
227 for (c, slot) in result.iter_mut().enumerate() {
228 let (a, b) = (part(arg(0), c), part(arg(1), c));
229 *slot = match op {
230 Op::Sub => a - b,
231 Op::Div => a / b,
232 Op::Pow => a.powf(b),
233 Op::Zip(f) => f.apply(a, b),
234 _ => unreachable!("the arm's own guard"),
235 };
236 }
237 }
238 Op::Map(f) => {
239 for (c, slot) in result.iter_mut().enumerate() {
240 *slot = f.apply(part(arg(0), c));
241 }
242 }
243 Op::Crop { a, b } => {
244 let inside = t >= *a && t < *b;
245 for (c, slot) in result.iter_mut().enumerate() {
246 *slot = if inside { part(arg(0), c) } else { 0.0 };
247 }
248 }
249 Op::Join(_) => {
250 let mut c = 0;
251 for k in 0..srcs.len() {
252 for &v in arg(k) {
253 result[c] = v;
254 c += 1;
255 }
256 }
257 }
258 Op::Channel(k) => result[0] = arg(0)[*k],
259 Op::Filter(id) => {
260 let State::Filter(filter) = &mut states[id.0 as usize] else {
261 unreachable!("a filter op names a filter site")
262 };
263 filter.process(arg(0), arg(1), arg(2), arg(3), result, sr, i);
264 }
265 Op::Physics(id) => {
266 let State::Physics(solver) = &mut states[id.0 as usize] else {
267 unreachable!("a physics op names a physics site")
268 };
269 result[0] = solver.step();
270 }
271 }
272}