Add ram_caps_oscs: separate memory caps for the per-osc state arena - #1107
Merged
Conversation
alloc_osc() draws the per-osc synthinfo/mod_synthinfo/breakpoint blocks from ram_caps_events. On MCUs those caps are usually chosen for the delta pool - fast internal RAM, since deltas are touched at every ingest and every block - and the osc arena inherits that placement despite a different profile: it grows lazily to the high-water mark of osc usage, never shrinks until amy_reset_oscs(), and is touched at control rate. Each default-sized osc costs 560 bytes, so a 25-osc-per-voice patch grows the arena by tens of KB with no way back short of a full reset. ram_caps_oscs defaults to ram_caps_events, so existing configs behave identically; targets with tiered memory can steer the arena separately. On an ESP32-S3 build, pointing it at PSRAM freed ~55 KB of internal SRAM at boot with render load unchanged (control-rate reads are cache-friendly; per-sample buffers stay on ram_caps_block/ram_caps_fbl).
Collaborator
⛓️ tulipcc integration PR openedThis merge was pinned into tulipcc for full-system CI: shorepine/tulipcc#1326 Test it there and merge that PR to move tulipcc onto this AMY. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
alloc_osc()draws the per-oscsynthinfo/mod_synthinfo/breakpointblocks from
ram_caps_events, so the arena and the delta pool arewelded to one placement despite opposite profiles: the delta pool is
walked at every ingest and every block and rewards the fastest RAM
available, while osc state is touched at control rate and is the larger
of the two by an order of magnitude on a busy scene. An app that steers
ram_caps_eventsto internal RAM for the delta pool's sake - myfirmware does exactly this - drags the whole arena with it; the default
ESP config (both on SPIRAM) welds them in the other direction. Freeing
released oscs (#1106) shrinks the arena's lifetime, but a
live heavy scene still parks its working set wherever this one knob
points, and the two purposes deserve separate answers.
This adds
ram_caps_oscsnext to the existing per-purpose caps(
ram_caps_synth,ram_caps_delay, ...). It defaults toram_caps_events, so existing configs behave identically; targets withtiered memory can steer the arena separately. A config that overrides
ram_caps_eventsafteramy_default_config()and wants the arena tofollow it now sets both fields.
Numbers (ESP32-S3, internal SRAM vs PSRAM tiers)
Each default-sized osc costs 560 B (
synthinfo372 +mod_synthinfo60 +2x
DEFAULT_NUM_BREAKPOINTSbreakpoint arrays at 8 B each; more once apatch grows a breakpoint set). The budget math is the whole argument:
filling the default pool (
max_oscs= 250) costs 250 x 560 B = ~137 KB.Welded to an internal-RAM delta pool, that has to fit in whatever
internal SRAM the app has left - my firmware had ~56 KB free, a hard
ceiling of ~100 oscs, 40% of the pool it nominally configured. With the
arena on PSRAM the same 137 KB is under 7% of even a 2 MB part, and the
ceiling goes back to being
max_oscsitself.With the arena on the delta pool's caps
(internal RAM), one 4-voice apply of the 25-osc-per-voice
piano.hpatch (100 oscs) consumed ~46 KB of internal SRAM - nearly my board's
whole free pool. Pointing
ram_caps_oscsat PSRAM freed ~55 KB ofinternal at boot (static synths) and cut the piano apply to the ~10 KB
that is genuinely delta-pool growth; render load was unchanged (per-block
state reads from PSRAM are cache-friendly at control rate; the per-sample
buffers stay wherever
ram_caps_block/ram_caps_fblpoint). The#1106 churn soaks add sustained evidence with the arena entirely
PSRAM-resident: 30 minutes of per-block render timing at a 1510 us mean
with first-100 and last-100 sample means identical (fixed scene), and
-0.2% render-mean drift between run halves under 90,000 randomized
configure/play/release ops (varying scene).
Notes for reviewers
The breakpoint-growth realloc in
ensure_osc_allocd()allocates thereplacement block through
alloc_osc(), so it picks up the same caps andan osc's state never migrates between tiers over its lifetime.