-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Experimenting towards allocator-free rendering #368
base: main
Are you sure you want to change the base?
Changes from all commits
4b19874
23b4d6a
614abc1
9d66b2a
c6146a9
b71f3cc
4a1bb98
de08796
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
use std::cell::RefCell; | ||
use std::rc::Rc; | ||
|
||
use std::any::Any; | ||
use std::time::Duration; | ||
|
||
type AnyChannel = llq::Producer<Box<dyn Any + Send>>; | ||
|
||
#[derive(Clone, Default)] | ||
pub(crate) struct GarbageCollector { | ||
gc: Option<Rc<RefCell<AnyChannel>>>, | ||
} | ||
|
||
impl GarbageCollector { | ||
pub fn deallocate_async(&self, value: llq::Node<Box<dyn Any + Send>>) { | ||
if let Some(gc) = self.gc.as_ref() { | ||
gc.borrow_mut().push(value); | ||
} | ||
} | ||
|
||
pub fn spawn_garbage_collector_thread(&mut self) { | ||
if self.gc.is_none() { | ||
let (gc_producer, gc_consumer) = llq::Queue::new().split(); | ||
spawn_garbage_collector_thread(gc_consumer); | ||
self.gc = Some(Rc::new(RefCell::new(gc_producer))); | ||
} | ||
} | ||
} | ||
|
||
// Controls the polling frequency of the garbage collector thread. | ||
const GARBAGE_COLLECTOR_THREAD_TIMEOUT: Duration = Duration::from_millis(100); | ||
|
||
// Poison pill that terminates the garbage collector thread. | ||
#[derive(Debug)] | ||
pub(crate) struct TerminateGarbageCollectorThread; | ||
|
||
// Spawns a sidecar thread of the `RenderThread` for dropping resources. | ||
fn spawn_garbage_collector_thread(consumer: llq::Consumer<Box<dyn Any + Send>>) { | ||
let _join_handle = std::thread::spawn(move || run_garbage_collector_thread(consumer)); | ||
} | ||
|
||
fn run_garbage_collector_thread(mut consumer: llq::Consumer<Box<dyn Any + Send>>) { | ||
log::debug!("Entering garbage collector thread"); | ||
loop { | ||
if let Some(node) = consumer.pop() { | ||
if node | ||
.as_ref() | ||
.downcast_ref::<TerminateGarbageCollectorThread>() | ||
.is_some() | ||
{ | ||
log::info!("Terminating garbage collector thread"); | ||
break; | ||
} | ||
// Implicitly drop the received node. | ||
} else { | ||
std::thread::sleep(GARBAGE_COLLECTOR_THREAD_TIMEOUT); | ||
} | ||
} | ||
log::info!("Exiting garbage collector thread"); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,9 @@ use super::{Alloc, AudioParamValues, AudioProcessor, AudioRenderQuantum, NodeCol | |
use crate::node::ChannelConfig; | ||
use crate::render::RenderScope; | ||
|
||
const INITIAL_GRAPH_SIZE: usize = 16; | ||
const INITIAL_CHANNEL_DATA_COUNT: usize = INITIAL_GRAPH_SIZE * 4; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would not per se solve the allocation problem, but taking a very large value for |
||
|
||
/// Connection between two audio nodes | ||
struct OutgoingEdge { | ||
/// index of the current Nodes output port | ||
|
@@ -27,9 +30,9 @@ pub struct Node { | |
/// Renderer: converts inputs to outputs | ||
processor: Box<dyn AudioProcessor>, | ||
/// Reusable input buffers | ||
inputs: Vec<AudioRenderQuantum>, | ||
inputs: SmallVec<[AudioRenderQuantum; 2]>, | ||
/// Reusable output buffers, consumed by subsequent Nodes in this graph | ||
outputs: Vec<AudioRenderQuantum>, | ||
outputs: SmallVec<[AudioRenderQuantum; 2]>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are not real solutions to the deallocation problems of this struct There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, maybe I'm missing something but it seems to me that the only nodes that have multiple inputs / outputs are the Then maybe we could simply use an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Scratch that, in the JS interface the https://webaudio.github.io/web-audio-api/#AudioWorkletNode is bound to 1 input and 1 output. So technically I could restrict the raw AudioNode trait and panic when the users supplies a 32+ input/output count. We need to check performance though, |
||
/// Channel configuration: determines up/down-mixing of inputs | ||
channel_config: ChannelConfig, | ||
/// Outgoing edges: tuple of outcoming node reference, our output index and their input index | ||
|
@@ -97,14 +100,14 @@ pub(crate) struct Graph { | |
impl Graph { | ||
pub fn new(reclaim_id_channel: llq::Producer<AudioNodeId>) -> Self { | ||
Graph { | ||
nodes: NodeCollection::new(), | ||
alloc: Alloc::with_capacity(64), | ||
nodes: NodeCollection::with_capacity(INITIAL_GRAPH_SIZE), | ||
alloc: Alloc::with_capacity(INITIAL_CHANNEL_DATA_COUNT), | ||
reclaim_id_channel, | ||
ordered: vec![], | ||
marked: vec![], | ||
marked_temp: vec![], | ||
in_cycle: vec![], | ||
cycle_breakers: vec![], | ||
ordered: Vec::with_capacity(INITIAL_GRAPH_SIZE), | ||
marked: Vec::with_capacity(INITIAL_GRAPH_SIZE), | ||
marked_temp: Vec::with_capacity(INITIAL_GRAPH_SIZE), | ||
in_cycle: Vec::with_capacity(INITIAL_GRAPH_SIZE), | ||
cycle_breakers: Vec::with_capacity(INITIAL_GRAPH_SIZE), | ||
} | ||
} | ||
|
||
|
@@ -127,8 +130,8 @@ impl Graph { | |
|
||
// set input and output buffers to single channel of silence, will be upmixed when | ||
// necessary | ||
let inputs = vec![AudioRenderQuantum::from(self.alloc.silence()); number_of_inputs]; | ||
let outputs = vec![AudioRenderQuantum::from(self.alloc.silence()); number_of_outputs]; | ||
let inputs = smallvec![AudioRenderQuantum::from(self.alloc.silence()); number_of_inputs]; | ||
let outputs = smallvec![AudioRenderQuantum::from(self.alloc.silence()); number_of_outputs]; | ||
|
||
self.nodes.insert( | ||
index, | ||
|
@@ -487,6 +490,7 @@ impl Graph { | |
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::render::GarbageCollector; | ||
|
||
#[derive(Debug, Clone)] | ||
struct TestNode { | ||
|
@@ -682,6 +686,7 @@ mod tests { | |
sample_rate: 48000., | ||
node_id: std::cell::Cell::new(AudioNodeId(0)), | ||
event_sender: None, | ||
garbage_collector: GarbageCollector::default(), | ||
}; | ||
graph.render(&scope); | ||
|
||
|
@@ -733,6 +738,7 @@ mod tests { | |
sample_rate: 48000., | ||
node_id: std::cell::Cell::new(AudioNodeId(0)), | ||
event_sender: None, | ||
garbage_collector: GarbageCollector::default(), | ||
}; | ||
graph.render(&scope); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a real solution to the problem