Skip to content

perf(studio): reuse the audio mix across optimistic edits - #258

Merged
LeadcodeDev merged 1 commit into
chantier/audit-2026-09from
perf/studio-audio-remix
Sep 22, 2026
Merged

LeadcodeDev merged 1 commit into
chantier/audit-2026-09from
perf/studio-audio-remix

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Severity Low, category quality. Location: crates/rustmotion-studio/src/editor/playback.rs:115

Impact

optimistic::commit bumps generation on EVERY mutation (one per oninput event), and use_hot_reload polls it every 250 ms, so a slider drag on a scenario that has audio calls audio::prepare ~4x/second. prepare spawns a brand-new std::thread that runs mix_audio_tracks over the whole scenario — documented in audio.rs:158 as "seconds on a long scenario" — decoding and resampling every track from scratch even though a font-size edit cannot have changed the audio. MIX_TOKEN only discards the RESULT; nothing cancels the work. Dozens of concurrent decode threads pile up, each holding a full interleaved f32 PCM buffer (~23 MB for 60 s stereo), fighting the 2-6 prefetch render workers for cores during exactly the interaction that most needs to stay smooth.

Fix

Only call prepare when the audio actually changed — compare a hash/fingerprint of scenario.audio plus total_duration against the last prepared one and return early otherwise. Optionally serialize mixing onto a single dedicated worker so a burst cannot spawn N threads.

Evidence the audit read

if last_gen != Some(g) {
    if last_gen.is_some() {
        rev.set(rev() + 1);
    }
    last_gen = Some(g);
    let fps = scenario.video.fps.max(1);
    super::audio::prepare(scenario, total as f64 / fps as f64);
}

Stacked on fix/studio-render-panic, which carries the previous finding of this workstream. GitHub shows only this finding's diff; merge in order.

Part of the September 2026 audit remediation chantier. Refs #220 (RM-39).

@LeadcodeDev LeadcodeDev added the bug Something isn't working label Sep 21, 2026
@LeadcodeDev LeadcodeDev self-assigned this Sep 21, 2026
@LeadcodeDev
LeadcodeDev force-pushed the fix/studio-render-panic branch from 452e5aa to 70fc5c6 Compare September 22, 2026 06:11
@LeadcodeDev
LeadcodeDev force-pushed the perf/studio-audio-remix branch from 792ade6 to 5ae2fc3 Compare September 22, 2026 06:11
@LeadcodeDev
LeadcodeDev force-pushed the fix/studio-render-panic branch from 70fc5c6 to 378aaef Compare September 22, 2026 08:35
@LeadcodeDev
LeadcodeDev force-pushed the perf/studio-audio-remix branch from 5ae2fc3 to 7c651d9 Compare September 22, 2026 08:36
@LeadcodeDev
LeadcodeDev force-pushed the fix/studio-render-panic branch from 378aaef to d85631b Compare September 22, 2026 08:45
@LeadcodeDev
LeadcodeDev force-pushed the perf/studio-audio-remix branch from 7c651d9 to 423648a Compare September 22, 2026 08:45
@LeadcodeDev
LeadcodeDev changed the base branch from fix/studio-render-panic to chantier/audit-2026-09 September 22, 2026 08:54
`optimistic::commit` bumps `generation` on EVERY mutation (one per `oninput` event), and `use_hot_reload` polls it every 250 ms, so a slider drag on a scenario that has audio calls `audio::prepare` ~4x/second. `prepare` spawns a brand-new `std::thread` that runs `mix_audio_tracks` over the whole scenario — documented in audio.rs:158 as "seconds on a long scenario" — decoding and resampling every track from scratch even though a font-size edit cannot have changed the audio. `MIX_TOKEN` only discards the RESULT; nothing cancels the work. Dozens of concurrent decode threads pile up, each holding a full interleaved f32 PCM buffer (~23 MB for 60 s stereo), fighting the 2-6 prefetch render workers for cores during exactly the interaction that most needs to stay smooth.

Refs #220
@LeadcodeDev
LeadcodeDev force-pushed the perf/studio-audio-remix branch from 423648a to 967354f Compare September 22, 2026 09:09
@LeadcodeDev
LeadcodeDev merged commit 29dbd16 into chantier/audit-2026-09 Sep 22, 2026
LeadcodeDev added a commit that referenced this pull request Sep 22, 2026
`optimistic::commit` bumps `generation` on EVERY mutation (one per `oninput` event), and `use_hot_reload` polls it every 250 ms, so a slider drag on a scenario that has audio calls `audio::prepare` ~4x/second. `prepare` spawns a brand-new `std::thread` that runs `mix_audio_tracks` over the whole scenario — documented in audio.rs:158 as "seconds on a long scenario" — decoding and resampling every track from scratch even though a font-size edit cannot have changed the audio. `MIX_TOKEN` only discards the RESULT; nothing cancels the work. Dozens of concurrent decode threads pile up, each holding a full interleaved f32 PCM buffer (~23 MB for 60 s stereo), fighting the 2-6 prefetch render workers for cores during exactly the interaction that most needs to stay smooth.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant