-
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?
Conversation
The goal is to run `cargo run --example audio_buffer_source_events` without failing for now
@@ -128,7 +128,7 @@ struct AudioParamEventTimeline { | |||
impl AudioParamEventTimeline { | |||
fn new() -> Self { | |||
Self { | |||
inner: Vec::new(), | |||
inner: Vec::with_capacity(5), |
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
/// 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 comment
The 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 comment
The 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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
@@ -11,6 +11,9 @@ use super::{Alloc, AudioParamValues, AudioProcessor, AudioRenderQuantum}; | |||
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 comment
The 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 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_events
without 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)