Skip to content

feat: Graph refactor chapter II#991

Open
poneciak57 wants to merge 60 commits into
mainfrom
feat/graph_refactor_2
Open

feat: Graph refactor chapter II#991
poneciak57 wants to merge 60 commits into
mainfrom
feat/graph_refactor_2

Conversation

@poneciak57
Copy link
Copy Markdown
Contributor

@poneciak57 poneciak57 commented Mar 19, 2026

Closes RNAA-433

⚠️ Breaking changes ⚠️

Introduced changes

Checklist

  • Linked relevant issue
  • Updated relevant documentation
  • Added/Conducted relevant tests
  • Performed self-review of the code
  • Updated Web Audio API coverage
  • Added support for web
  • Updated old arch android spec file

@maciejmakowski2003 maciejmakowski2003 force-pushed the feat/graph_refactor_2 branch 2 times, most recently from 62ae703 to 71f1fd0 Compare March 31, 2026 06:37
@mdydek mdydek marked this pull request as ready for review May 7, 2026 09:34
@mdydek mdydek added the feature New feature label May 7, 2026
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just checking in
is it possible to call start and stop concurently (eg at the same time)?

Copy link
Copy Markdown
Contributor Author

@poneciak57 poneciak57 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overally it looks really great. One global issue is lack of high level documentation. It would be great to write/generate some high level design doc on top of that or keep existing ones in sync with the code

But as i said overally good job 👍

Comment on lines +35 to +36
delay_ring::bufferOperation(
delayBuffer, audioBuffer_, framesToProcess, writeIndex, delay_ring::BufferAction::WRITE);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why u have Action type instead of having two methods write/read?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because it shares a lot of code under the hood, but it would be hard to split it into two separate functions and put common code elsewhere, this way we can easily control what is shared and what is not

@@ -26,7 +24,7 @@ ConvolverNode::ConvolverNode(
void ConvolverNode::setBuffer(
const std::shared_ptr<AudioBuffer> &buffer,
std::vector<std::unique_ptr<Convolver>> convolvers,
const std::shared_ptr<ThreadPool> &threadPool,
const std::shared_ptr<ThreadPool<32>> &threadPool,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

32 is deffinitly too much
threads are used here for compute heavy operations so should be determined by hardware_concurrency. Otherwise they will hog cpu and steal power from each other.
Usually it does not matter that much but here they are not sleeping theyre job is to compute

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i get that this number can be confusing, but its not amount of threads, but task size so threadpool can be safely used on audio thread

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sure this file is compiled with -O3 -ffast-math

Comment on lines +70 to +78
/// Envelope threshold at which the impulse response is considered inaudible (-80 dB).
static constexpr double kTailEpsilon = 1e-4;

/// Hard upper bound on the computed tail length (seconds).
static constexpr float kMaxTailSeconds = 30.0f;

/// Keeps `log(r)` bounded away from zero when the pole sits on or outside
/// the unit circle.
static constexpr double kPoleRadiusEpsilon = 1e-5;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constexpr does not need to be marked static

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but it is inside of class,
"Non-static data member cannot be constexpr; did you intend to make it static? (fix available)clang(invalid_constexpr_member)"

@@ -108,7 +108,7 @@ class PromiseVendor {
private:
jsi::Runtime *runtime_;
std::shared_ptr<react::CallInvoker> callInvoker_;
std::shared_ptr<ThreadPool> threadPool_;
std::shared_ptr<ThreadPool<96>> threadPool_;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

96?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rewrote threadpool to fatfunction instead of moveonly function and task size needs to be passed in threadpool with new changes

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now it should be slightly more informative

Comment on lines +38 to +65
auto drainA = [&] {
AGEvent ev;
while (eventReceiver_.try_receive(ev) == ResponseStatus::SUCCESS) {
if (ev) {
ev(audioGraph, *disposer_);
}
}
};

// Steady-state: drain any A events queued since the last cycle.
drainA();

// For every pending orphan on Channel B: ensure Channel A's receive
// cursor has reached the barrier the GC thread snapshotted at orphan
// enqueue time. Until that barrier is met, every A event that the
// orphan happens-after must still be in flight; we drain A again to
// catch up. Only then do we consume and apply the orphan.
while (const OrphanEnvelope *front = gcEventReceiver_.try_peek()) {
if (eventReceiver_.rcvCursor() < front->barrier) {
drainA();
if (eventReceiver_.rcvCursor() < front->barrier) {
// Producer is mid-send on Channel A; the event will arrive
// imminently. Defer this orphan to the next processEvents()
// tick rather than burning audio-thread cycles spinning.
break;
}
}
OrphanEnvelope consumed;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this "vector-clock-like" aproach is great, but the logic seems to be split between few places. It would be nice to maybe make some wrapper on top of the SPSC to handle that scenario

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants