Revert "Merge Ω-W2-T4: split the deferred FX-park drain by kind"
This reverts commit70bd29dc89, reversing changes made to84afd1b545.
This commit is contained in:
@@ -54,38 +54,14 @@ decide membership or mode rules.
|
||||
lost. What it does change is the undo record: the offline writes land outside
|
||||
the switch's undo block, so the tool no longer re-drives them on an undo or a
|
||||
redo — what a Ctrl-Z then leaves the chain at is REAPER's own FX-state record,
|
||||
`[verify — DAW]`. **The drain is SPLIT BY INTENT KIND, because only one kind is
|
||||
unsafe to persist over.** A pending RESTORE is: flags restored, snapshot already
|
||||
dropped, FX still offline — a save inside that window records offline FX beside a
|
||||
model that can no longer replan them, unrecoverable on reopen. `persistViewState`
|
||||
and `render_in_place` are the only two paths that force the restore drain before
|
||||
they serialize; roughly ten other `saveToActiveProject` callers (`ingest.cpp`,
|
||||
`bank_ops.cpp`, `bake_land.cpp`, `capture_batch.cpp`, `capture_orchestrator.cpp`,
|
||||
`realtime_lifecycle.cpp`, `panel_input.cpp`) persist without forcing it first — a
|
||||
pre-existing, roughly one-tick-wide gap (the idle tick drains restores too),
|
||||
not known to be exploitable in practice. A new caller that reapplies a mode and
|
||||
then persists inherits the forced-drain obligation; one that merely persists does
|
||||
not. `drainDeferredFxRestores` is also a silent no-op while a drain is already in
|
||||
flight (`g_draining`), so a restore forced from inside another drain's
|
||||
message-pump window can itself be skipped — same order of window, same
|
||||
pre-existing status. An action-driven switch still pays the RELOAD hitch before
|
||||
it returns — its repaint and undo block have both closed by then, which is what
|
||||
the deferral was for. A pending PARK is safe to save over: flags parked, snapshot
|
||||
stored, FX still online, so a reopen replans the park and the drain offlines it —
|
||||
it converges. Parks therefore go lazy, ONE FX per idle tick behind a one-second
|
||||
coalescing delay, which bounds each tick's hitch to one FX's unload rather than
|
||||
eliminating it — see `view_fx_park.h` for why per-track chunking would not help.
|
||||
CPU reclaim is therefore PROGRESSIVE, completing over FX-count ticks rather than
|
||||
in one step: a user watching the CPU meter after a switch sees a ramp, not a
|
||||
drop — in tension with `design-view.md`'s full-CPU-park rationale ("a heavy FX
|
||||
bench you don't want taxing the CPU while you arrange"). The delay is only ONE term
|
||||
of the live-enumeration hazard's width: `firstOnlineFx` re-enumerates the chain
|
||||
every tick, not just the first, so the actual exposure is delay + (FX count ×
|
||||
tick interval), and for a large chain the second term dominates — see
|
||||
`tickDeferredFxParks`. Do not lengthen the delay casually.
|
||||
`[verify — DAW]` whether the switch's `UpdateArrange` paints before the first
|
||||
drain tick: nothing pumps the message loop between them, so "the switch paints
|
||||
first" is an assumption, not an SDK guarantee.
|
||||
`[verify — DAW]`. **The idle tick is not the only drain point.** Any path that
|
||||
serializes the view model drains synchronously first (`persistViewState`,
|
||||
`render_in_place`), because a save landing between a restore's synchronous flag
|
||||
writes and its drain would record offline FX beside a model that no longer
|
||||
carries the snapshot to replan them — unrecoverable on reopen. So an
|
||||
action-driven switch does pay the FX hitch before it returns; the repaint and
|
||||
the undo block have both closed by then, which is what the deferral was for.
|
||||
Any new caller that reapplies a mode and then persists inherits this obligation.
|
||||
- **Stated DEVIATION — the undo mask does not keep FX out of a real switch.** The
|
||||
apply mask (`kApplyUndoMask`) drops `UNDO_STATE_FX` and ORs it back in when a
|
||||
driven flag in that domain moved; the only such flag is `I_FXEN`, which every
|
||||
@@ -131,7 +107,7 @@ applies the resulting lane state to live tracks.
|
||||
## Modules
|
||||
|
||||
- `view` — Design View shell: snapshots flag values before parking, drives hide + CPU-park on inactive-mode leaves (`B_SHOWINTCP`/`B_SHOWINMIXER`/`B_MAINSEND`/`I_FXEN`, with per-FX offline deferred to `view_fx_park`), restores from snapshot. Owns the one discriminator (`target != active`) that separates a real switch from a reapply, and with it both the playback gate (`transportBlocksModeSwitch`) and the solo cache/clear/restore seams. **Never touches master or `B_MUTE`.**
|
||||
- `view_fx_park` — the per-FX offline surface: the `TrackFX_GetFXGUID` identity read snapshot/park/restore share, the deferred intent queue that keeps `TrackFX_SetOffline` off the switch's synchronous path (at most one intent per track GUID, latest wins, an intent landing on its own pending inverse cancels it — which is what makes a rapid A→B→A flip cost zero plugin work), and the two drains `main.cpp`'s `OnTimer` calls: `drainDeferredFxRestores` (whole, also forced by every persist path) and `tickDeferredFxParks` (one FX per tick, after a coalescing delay). A park that has already written one FX can no longer be cancelled outright in either direction — the live chain then matches neither endpoint, so the intent carries a `partial` flag and its inverse SUPERSEDES it instead. **The drains own no model state.** A snapshot is dropped where the restore is PLANNED — the flags are back at their captured values from that moment, and a model that still described the track as parked would let a persist or a reapply inside the drain window replan a restore over whatever the user changed since. What the deferral costs instead is that the live FX chain stops being a trustworthy snapshot source while an intent is pending: a park that CANCELS a pending restore takes the pre-park FX states from that restore's ops (`preParkFxFromCancelledRestore`), because the chain still reads the parked values and the cancel means no drain will ever fix them.
|
||||
- `view_fx_park` — the per-FX offline surface: the `TrackFX_GetFXGUID` identity read snapshot/park/restore share, the deferred intent queue that keeps `TrackFX_SetOffline` off the switch's synchronous path (at most one intent per track GUID, latest wins, an intent landing on its own pending inverse cancels it), and the idle-tick drain `main.cpp`'s `OnTimer` calls. **The drain owns no model state.** A snapshot is dropped where the restore is PLANNED — the flags are back at their captured values from that moment, and a model that still described the track as parked would let a persist or a reapply inside the drain window replan a restore over whatever the user changed since. What the deferral costs instead is that the live FX chain stops being a trustworthy snapshot source while an intent is pending: a park that CANCELS a pending restore takes the pre-park FX states from that restore's ops (`preParkFxFromCancelledRestore`), because the chain still reads the parked values and the cancel means no drain will ever fix them.
|
||||
- `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`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
Reference in New Issue
Block a user