Experimenting towards allocator-free rendering#368
Conversation
The goal is to run `cargo run --example audio_buffer_source_events` without failing for now
| fn new() -> Self { | ||
| Self { | ||
| inner: Vec::new(), | ||
| inner: Vec::with_capacity(5), |
There was a problem hiding this comment.
This is not a real solution to the problem
| 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.
These are not real solutions to the deallocation problems of this struct
There was a problem hiding this comment.
Actually, maybe I'm missing something but it seems to me that the only nodes that have multiple inputs / outputs are the ChannelSplitterNode and ChannelMergerNode which are clamped to MAX_CHANNELS, cf. https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createchannelmerger, no?
Then maybe we could simply use an ArrayVec<AudioRenderQuantum, MAX_CHANNELS> to fix this?
There was a problem hiding this comment.
True that, but of course we also allow users to implement their own AudioNodes which I think could use an unbounded number of inputs/outputs.
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, ArrayVec<AudioRenderQuantum, MAX_CHANNELS> is a rather large object to have on the stack, and rust will probably memcpy it around a lot, so we may want to have it on the heap anyway. We can experiment with pre-allocating that Vec on the control thread
| 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.
Would not per se solve the allocation problem, but taking a very large value for INITIAL_GRAPH_SIZE could help (4096 or 8185), as vector growth seems to be exponential (cf. https://nnethercote.github.io/perf-book/heap-allocations.html?highlight=borrow#vec-growth)
|
Cool, this is very interesting! I will try to have a closer look on this and the previous PR this week-end (I'm a bit running to find some time these weeks...) |
…render' into feature/store-nodes-in-vec-not-hashmap
|
Hey, I just made a small hacky experiment on how a renderer could just drop itself in GC with That's rather weird I must confess :), but it seems to work... maybe this can give some ideas |
Well, this is definitely a new use case for recursive data structures. Thanks ;) However, I'm getting more and more convinced that the pub fn deallocate_audio_processor(&self, value: Box<dyn AudioProcessor>) {
let p = Box::into_raw(value);
unsafe {
ptr::drop_in_place(p); // drop the actual AudioProcessor fields
dealloc(p as *mut u8, Layout::new::<Box<dyn AudioProcessor>>()); // TODO - perform this call by GC thread
}
}The reason I want the processor to drop inside the render thread is that I don't want to put the burden of clearing your Let me share my current plans: |
|
ok, not sure I understand every detail (...pretty sure I actually don't, let's be honest), but looking forward to see the thing :) |
|
(my experiment is quite fun anyway! :) |
The goal is to run
cargo run --example audio_buffer_source_eventswithout failing for now.It shows that the #366 works like intended, there is no deallocation of the audio buffer taking place.
But this PR made me realize we also need to look into all
Arc<..>items of renderers, because it now fails withAnd then there's still more issues to look at (
Box<dyn AudioProcessor>for example)