Close the FX reclaimer's undo span at the state-load hook, and expose the drain's direction
A Ctrl-Z mid-span used to close the pre-undo block over post-undo state; it now terminates at BeginLoadProjectState and stops the drain where it stands. fxReclaimActivity reports Online/Offline/Idle; fxReclaimPending is unchanged.
This commit is contained in:
@@ -84,12 +84,25 @@ decide membership or mode rules.
|
||||
block swallows every edit the user makes afterwards, indefinitely, so every
|
||||
terminating path routes through ONE idempotent close (`fxReclaimEndUndoSpan`): the
|
||||
drain settling, aborting on a generation bump, or deferring on the transport; the
|
||||
project retargeting or closing; the next apply superseding it; a project save;
|
||||
extension unload. The span may stay open only across a wait it can BOUND — the 3s
|
||||
debounce, which is the dead time between the switch and its first FX write, and
|
||||
the only reason the span exists. A TRANSPORT deferral is not bounded (it lasts as
|
||||
long as the playback pass), so it ends the span and the parking that follows mints
|
||||
its own point (`spanEndsAfter`).
|
||||
project retargeting or closing; the next apply superseding it; a USER-DRIVEN STATE
|
||||
LOAD; a project save; extension unload. The span may stay open only across a wait it
|
||||
can BOUND — the 3s debounce, which is the dead time between the switch and its first
|
||||
FX write, and the only reason the span exists. A TRANSPORT deferral is not bounded
|
||||
(it lasts as long as the playback pass), so it ends the span and the parking that
|
||||
follows mints its own point (`spanEndsAfter`).
|
||||
- **A Ctrl-Z terminates the span itself, at the hook, before REAPER restores
|
||||
anything.** `fxReclaimAbandonForStateLoad` runs from `BeginLoadProjectState`
|
||||
(`main.cpp`) — the only point at which the block still closes over the state it was
|
||||
OPENED on. The span is open for at least `kParkHoldMs` after every real switch and a
|
||||
switch is exactly what a user undoes, so letting the close happen where it used to —
|
||||
as a side effect of the reapply the reloaded model triggers, a full timer tick later
|
||||
— closed the pre-undo block over POST-undo state, minting a spurious point and
|
||||
destroying the redo branch. The same call stops any drain on the stack where it
|
||||
stands (`FxReclaimQueue::abandon`, refused at both host entry points), which is
|
||||
stronger than an invalidation: an invalidation still finishes the track it is on,
|
||||
because a step is atomic. Daniel's ruling, and the accepted cost: undoing a switch
|
||||
mid-drain leaves a PARTIAL park — some FX offlined, some not — covered by the one
|
||||
point the terminator closed.
|
||||
- **A reapply that wrote nothing leaves no undo point** — and a reapply never holds
|
||||
the span open, so a project open, a membership edit and `render_in_place`'s inner
|
||||
reapply all keep the block shape they had. The load-tick reapply
|
||||
@@ -148,7 +161,7 @@ applies the resulting lane state to live tracks.
|
||||
- `view_reconcile` — the repair pass for a project state REAPER reloaded under us: `noteAppliedState` records what an apply left driven; `reconcileAppliedFlags` diffs that fingerprint against the reloaded model and hands back any track the reload no longer records but still reads driven — the fix for `UNDO_STATE_TRACKCFG` not carrying `B_SHOWINTCP`/`B_SHOWINMIXER` (DAW-confirmed).
|
||||
- `view_tracks` — the scaffolding every write pass sets up: `TrackHandles`/`TrackByGuid`, `readFolderEntries`/`indexHandles`, and `UiRefreshHold` (the `PreventUIRefresh` RAII pair every write phase runs under).
|
||||
- `view_solo` — the `I_SOLO` read/write pair behind the per-mode solo surface, plus `clearTrackSolos`/`restoreTrackSolos`, the outgoing-clear and incoming-replay entry points `view` drives them through. Holds no policy: what to cache, clear, or replay is `core/view/solo_cache`.
|
||||
- `fx_reclaim` — the FX convergence loop, off the switch's synchronous path (`TrackFX_SetOffline` was 1897ms of a measured 1942ms switch). `drainTick` moves one track's FX per step against `core/view/fx_state`'s derived work list (Online before Offline; an Offline item waits out `kParkHoldMs` so a fast A→B→A flip moves nothing), budgeted per tick (`kOnlineBudgetMs`/`kOfflineBudgetMs`) and gated off the transport for the Offline direction only. `FxReclaimQueue` owns the derived queue's lifetime across re-entrancy (a plugin unload is assumed to pump). `UndoSpan` owns the ONE undo block the whole drain writes under — opened by `applyMode` on a real switch, else lazily by the first `writeOffline`, and closed through the single idempotent `fxReclaimEndUndoSpan` that every terminating path routes through (`spanEndsAfter` decides which ticks terminate). `ReleaseFlush`/`fxReclaimFlushRelease` make a completed record's spend durable on the project's next save, never on the drain's own initiative.
|
||||
- `fx_reclaim` — the FX convergence loop, off the switch's synchronous path (`TrackFX_SetOffline` was 1897ms of a measured 1942ms switch). `drainTick` moves one track's FX per step against `core/view/fx_state`'s derived work list (Online before Offline; an Offline item waits out `kParkHoldMs` so a fast A→B→A flip moves nothing), budgeted per tick (`kOnlineBudgetMs`/`kOfflineBudgetMs`) and gated off the transport for the Offline direction only. `FxReclaimQueue` owns the derived queue's lifetime across re-entrancy (a plugin unload is assumed to pump). `UndoSpan` owns the ONE undo block the whole drain writes under — opened by `applyMode` on a real switch, else lazily by the first `writeOffline`, and closed through the single idempotent `fxReclaimEndUndoSpan` that every terminating path routes through (`spanEndsAfter` decides which ticks terminate; `fxReclaimAbandonForStateLoad` is the undo/redo terminator, which also stops the drain). `ReleaseFlush`/`fxReclaimFlushRelease` make a completed record's spend durable on the project's next save, never on the drain's own initiative. `fxReclaimActivity` reports WHICH DIRECTION the drain is moving for an indicator to paint — Idle whenever nothing may move, so a debounced or transport-held offline item never names a direction nothing is travelling in; undirected `fxReclaimPending` stays the timer's one idle test and must not drive a UI (it is true throughout the debounce, when only the OPPOSITE direction is eligible).
|
||||
|
||||
## Gotchas
|
||||
|
||||
@@ -182,8 +195,9 @@ applies the resulting lane state to live tracks.
|
||||
flip — still closes on `kViewUndoMask` alone and costs no FX serialization. What a
|
||||
Ctrl-Z cannot restore is anything the reclaimer converges AFTER the span closed: a
|
||||
transport deferral, and a drain no switch opened a span for, each mint their own
|
||||
point. Whether that reads as correct in the DAW is `[verify — DAW]`,
|
||||
`docs/VERIFICATION.md` §"Mode switching".
|
||||
point — and, since the terminator above, whatever the drain had NOT yet written when
|
||||
the undo arrived, because the span closes at that instant. Whether that reads as
|
||||
correct in the DAW is `[verify — DAW]`, `docs/VERIFICATION.md` §"Mode switching".
|
||||
- **The `view_state` write is NOT inside `applyMode`'s block — but on a REAL switch
|
||||
it is now inside the SPAN.** `persistViewState` (`design_view_actions.cpp`) runs
|
||||
after `applyMode` has returned, and a real switch returns with the span open, so
|
||||
|
||||
Reference in New Issue
Block a user