1use alloc::{collections::VecDeque, rc::Rc};
2use firewheel_core::node::{AudioNodeInfoInner, DynAudioNode, NodeID};
3use smallvec::SmallVec;
4use thunderdome::Arena;
5
6#[cfg(not(feature = "std"))]
7use bevy_platform::prelude::{Box, Vec, vec};
8
9use crate::error::CompileGraphError;
10
11mod schedule;
12
13pub(crate) use schedule::{CompiledSchedule, NodeHeapData, ProcessNodeInfo, ScheduleHeapData};
14use schedule::{InBufferAssignment, OutBufferAssignment, PreProcNode, ScheduledNode};
15
16pub struct NodeEntry {
17 pub id: NodeID,
18 pub info: AudioNodeInfoInner,
19 pub dyn_node: Box<dyn DynAudioNode>,
22 pub processor_constructed: bool,
23 incoming: SmallVec<[Edge; 4]>,
25 outgoing: SmallVec<[Edge; 4]>,
27}
28
29impl NodeEntry {
30 pub fn new(mut info: AudioNodeInfoInner, dyn_node: Box<dyn DynAudioNode>) -> Self {
31 if info.channel_config.num_outputs.get() == 0 {
32 info.in_place_buffers = false;
33 }
34
35 Self {
36 id: NodeID::DANGLING,
37 info,
38 dyn_node,
39 processor_constructed: false,
40 incoming: SmallVec::new(),
41 outgoing: SmallVec::new(),
42 }
43 }
44}
45
46pub type PortIdx = u32;
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub struct EdgeID(pub(super) thunderdome::Index);
52
53#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
56pub struct Edge {
57 pub id: EdgeID,
58 pub src_node: NodeID,
60 pub src_port: PortIdx,
62 pub dst_node: NodeID,
64 pub dst_port: PortIdx,
66}
67
68#[derive(Debug, Clone, Copy)]
70struct BufferRef {
71 idx: usize,
73 generation: usize,
76}
77
78#[derive(Debug, Clone)]
80struct BufferAllocator {
81 free_list: Vec<BufferRef>,
83 count: usize,
85}
86
87impl BufferAllocator {
88 fn new(initial_capacity: usize) -> Self {
91 Self {
92 free_list: Vec::with_capacity(initial_capacity),
93 count: 0,
94 }
95 }
96
97 fn acquire(&mut self) -> Rc<BufferRef> {
99 let entry = self.free_list.pop().unwrap_or_else(|| {
100 let idx = self.count;
101 self.count += 1;
102 BufferRef { idx, generation: 0 }
103 });
104 Rc::new(BufferRef {
105 idx: entry.idx,
106 generation: entry.generation,
107 })
108 }
109
110 fn release(&mut self, buffer_ref: Rc<BufferRef>) {
112 if Rc::strong_count(&buffer_ref) == 1 {
113 self.free_list.push(BufferRef {
114 idx: buffer_ref.idx,
115 generation: buffer_ref.generation + 1,
116 });
117 }
118 }
119
120 fn num_buffers(self) -> usize {
122 self.count
123 }
124}
125
126pub fn compile(
128 nodes: &mut Arena<NodeEntry>,
129 edges: &mut Arena<Edge>,
130 graph_in_id: NodeID,
131 graph_out_id: NodeID,
132 max_block_frames: usize,
133 prev_buffer_capacity: usize,
134) -> Result<CompiledSchedule, CompileGraphError> {
135 Ok(GraphIR::preprocess(
136 nodes,
137 edges,
138 graph_in_id,
139 graph_out_id,
140 max_block_frames,
141 prev_buffer_capacity,
142 )
143 .sort_topologically(true)?
144 .solve_buffer_requirements()?
145 .merge())
146}
147
148pub fn cycle_detected<'a>(
149 nodes: &'a mut Arena<NodeEntry>,
150 edges: &'a mut Arena<Edge>,
151 graph_in_id: NodeID,
152 graph_out_id: NodeID,
153) -> bool {
154 matches!(
155 GraphIR::preprocess(nodes, edges, graph_in_id, graph_out_id, 0, 0)
156 .sort_topologically(false),
157 Err(CompileGraphError::CycleDetected)
158 )
159}
160
161struct GraphIR<'a> {
164 nodes: &'a mut Arena<NodeEntry>,
165 edges: &'a mut Arena<Edge>,
166
167 pre_proc_nodes: Vec<PreProcNode>,
170 schedule: Vec<ScheduledNode>,
172 max_num_buffers: usize,
174
175 graph_in_id: NodeID,
176 graph_out_id: NodeID,
177 max_in_buffers: usize,
178 max_out_buffers: usize,
179 max_block_frames: usize,
180
181 prev_buffer_capacity: usize,
182}
183
184impl<'a> GraphIR<'a> {
185 fn preprocess(
188 nodes: &'a mut Arena<NodeEntry>,
189 edges: &'a mut Arena<Edge>,
190 graph_in_id: NodeID,
191 graph_out_id: NodeID,
192 max_block_frames: usize,
193 prev_buffer_capacity: usize,
194 ) -> Self {
195 assert!(nodes.contains(graph_in_id.0));
196 assert!(nodes.contains(graph_out_id.0));
197
198 for (_, node) in nodes.iter_mut() {
199 node.incoming.clear();
200 node.outgoing.clear();
201 }
202
203 for (_, edge) in edges.iter() {
204 nodes[edge.src_node.0].outgoing.push(*edge);
205 nodes[edge.dst_node.0].incoming.push(*edge);
206
207 debug_assert_ne!(edge.src_node, graph_out_id);
208 debug_assert_ne!(edge.dst_node, graph_in_id);
209 }
210
211 Self {
212 nodes,
213 edges,
214 pre_proc_nodes: vec![],
215 schedule: vec![],
216 max_num_buffers: 0,
217 graph_in_id,
218 graph_out_id,
219 max_in_buffers: 0,
220 max_out_buffers: 0,
221 max_block_frames,
222 prev_buffer_capacity,
223 }
224 }
225
226 fn sort_topologically(mut self, build_schedule: bool) -> Result<Self, CompileGraphError> {
229 let mut in_degree = vec![0i32; self.nodes.capacity()];
230 let mut queue = VecDeque::with_capacity(self.nodes.len());
231
232 if build_schedule {
233 self.schedule.reserve(self.nodes.len());
234 }
235
236 let mut num_visited = 0;
237
238 for (_, node_entry) in self.nodes.iter() {
240 for edge in node_entry.outgoing.iter() {
241 in_degree[edge.dst_node.0.slot() as usize] += 1;
242 }
243 }
244
245 queue.push_back(self.graph_in_id.0.slot());
249
250 for (_, node_entry) in self.nodes.iter() {
252 if node_entry.incoming.is_empty() && node_entry.id.0.slot() != self.graph_in_id.0.slot()
253 {
254 if node_entry.info.channel_config.is_empty() {
257 self.pre_proc_nodes.push(PreProcNode {
258 id: node_entry.id,
259 debug_name: node_entry.info.debug_name,
260 });
261
262 num_visited += 1;
263 } else {
264 queue.push_back(node_entry.id.0.slot());
265 }
266 }
267 }
268
269 while let Some(node_slot) = queue.pop_front() {
271 num_visited += 1;
272
273 let (_, node_entry) = self.nodes.get_by_slot(node_slot).unwrap();
274
275 for edge in node_entry.outgoing.iter() {
277 in_degree[edge.dst_node.0.slot() as usize] -= 1;
278
279 if in_degree[edge.dst_node.0.slot() as usize] == 0 {
281 queue.push_back(edge.dst_node.0.slot());
282 }
283 }
284
285 if build_schedule && node_slot != self.graph_out_id.0.slot() {
286 self.schedule.push(ScheduledNode::new(
287 node_entry.id,
288 node_entry.info.debug_name,
289 node_entry.info.in_place_buffers,
290 ));
291 }
292 }
293
294 if build_schedule {
295 self.schedule
300 .push(ScheduledNode::new(self.graph_out_id, "graph_out", false));
301 }
302
303 if num_visited != self.nodes.len() {
305 return Err(CompileGraphError::CycleDetected);
306 }
307
308 Ok(self)
309 }
310
311 fn solve_buffer_requirements(mut self) -> Result<Self, CompileGraphError> {
312 let mut allocator = BufferAllocator::new(64);
313 let mut assignment_table: Arena<Rc<BufferRef>> =
314 Arena::with_capacity(self.edges.capacity());
315 let mut buffers_to_release: Vec<Rc<BufferRef>> = Vec::with_capacity(64);
316
317 for entry in &mut self.schedule {
318 let node_entry = &self.nodes[entry.id.0];
321
322 let num_inputs = node_entry.info.channel_config.num_inputs.get() as usize;
323 let num_outputs = node_entry.info.channel_config.num_outputs.get() as usize;
324
325 buffers_to_release.clear();
326 if buffers_to_release.capacity() < num_inputs + num_outputs {
327 buffers_to_release
328 .reserve(num_inputs + num_outputs - buffers_to_release.capacity());
329 }
330
331 entry.input_buffers.reserve_exact(num_inputs);
332 entry.output_buffers.reserve_exact(num_outputs);
333
334 for port_idx in 0..num_inputs as u32 {
335 let edges: SmallVec<[&Edge; 4]> = node_entry
336 .incoming
337 .iter()
338 .filter(|edge| edge.dst_port == port_idx)
339 .collect();
340
341 entry
342 .in_connected_mask
343 .set_channel(port_idx as usize, !edges.is_empty());
344
345 if edges.is_empty() {
346 let buffer = allocator.acquire();
350 entry.input_buffers.push(InBufferAssignment {
351 buffer_index: buffer.idx,
352 should_clear: true,
354 });
355 buffers_to_release.push(buffer);
356 } else if edges.len() == 1 {
357 let buffer = assignment_table
361 .remove(edges[0].id.0)
362 .expect("No buffer assigned to edge!");
363 entry.input_buffers.push(InBufferAssignment {
364 buffer_index: buffer.idx,
365 should_clear: false,
367 });
368 buffers_to_release.push(buffer);
369 } else {
370 let sum_buffer = allocator.acquire();
375 let sum_output = OutBufferAssignment {
376 buffer_index: sum_buffer.idx,
377 };
379
380 let sum_inputs = edges
382 .iter()
383 .map(|edge| {
384 let buf = assignment_table
385 .remove(edge.id.0)
386 .expect("No buffer assigned to edge!");
387 let assignment = InBufferAssignment {
388 buffer_index: buf.idx,
389 should_clear: false,
391 };
392 allocator.release(buf);
393 assignment
394 })
395 .collect();
396
397 entry.sum_inputs.push(InsertedSum {
398 input_buffers: sum_inputs,
399 output_buffer: sum_output,
400 });
401
402 entry.input_buffers.push(InBufferAssignment {
405 buffer_index: sum_output.buffer_index,
406 should_clear: false,
408 });
409
410 buffers_to_release.push(sum_buffer);
411 }
412 }
413
414 for port_idx in 0..num_outputs as u32 {
415 let edges: SmallVec<[&Edge; 4]> = node_entry
416 .outgoing
417 .iter()
418 .filter(|edge| edge.src_port == port_idx)
419 .collect();
420
421 entry
422 .out_connected_mask
423 .set_channel(port_idx as usize, !edges.is_empty());
424
425 if edges.is_empty() {
426 let buffer = allocator.acquire();
430 entry.output_buffers.push(OutBufferAssignment {
431 buffer_index: buffer.idx,
432 });
434 buffers_to_release.push(buffer);
435 } else {
436 let buffer = allocator.acquire();
440 for edge in &edges {
441 assignment_table.insert_at(edge.id.0, Rc::clone(&buffer));
442 }
443 entry.output_buffers.push(OutBufferAssignment {
444 buffer_index: buffer.idx,
445 });
447 }
448 }
449
450 for buffer in buffers_to_release.drain(..) {
451 allocator.release(buffer);
452 }
453
454 self.max_in_buffers = self.max_in_buffers.max(num_inputs);
455 self.max_out_buffers = self.max_out_buffers.max(num_outputs);
456 }
457
458 self.max_num_buffers = allocator.num_buffers();
459 Ok(self)
460 }
461
462 fn merge(self) -> CompiledSchedule {
464 CompiledSchedule::new(
465 self.pre_proc_nodes,
466 self.schedule,
467 self.max_num_buffers,
468 self.max_out_buffers,
469 self.max_block_frames,
470 self.graph_in_id,
471 self.prev_buffer_capacity,
472 )
473 }
474}
475
476#[derive(Debug, Clone)]
477struct InsertedSum {
478 input_buffers: SmallVec<[InBufferAssignment; 4]>,
479 output_buffer: OutBufferAssignment,
480}