view: assert the FX-park coalescing delay's debounce; correct four overclaiming doc/comment claims
Extracts parkReadyAt/parkIsReady as a tested pure fold per PLAN.md's phase criterion; the rest is wording fixes — hitch bound, hazard width, forced-drain scope, progressive CPU reclaim.
This commit is contained in:
@@ -16,6 +16,15 @@ class ReaProject;
|
||||
|
||||
namespace reasampler {
|
||||
|
||||
// The coalescing delay's gate, pure so the debounce can be asserted without a
|
||||
// DAW clock (test_view_fx_park.cpp). parkReadyAt is called on EVERY enqueue,
|
||||
// not just the first — that re-arming from each call's own `now` is what makes
|
||||
// it a debounce rather than a one-shot timer, and is what lets a rapid A→B→A
|
||||
// flip (three enqueues inside one delay window) cost a single wait measured
|
||||
// from the last flip rather than three, or one anchored to the first.
|
||||
inline double parkReadyAt(double now, double coalesceSeconds) { return now + coalesceSeconds; }
|
||||
inline bool parkIsReady(double now, double readyAt) { return now >= readyAt; }
|
||||
|
||||
// The chain as it stands now: identity by current slot. Snapshot, park and
|
||||
// restore all address FX through this one plain 0..TrackFX_GetCount-1
|
||||
// enumeration — never the 0x1000000/0x2000000 input-FX or container forms — so
|
||||
@@ -27,14 +36,11 @@ std::vector<std::string> liveFxGuids(MediaTrack* tr);
|
||||
struct FxParkIntent {
|
||||
std::string guid;
|
||||
bool park = false;
|
||||
// A park write is landing or has landed on this track, so the live chain
|
||||
// matches NEITHER endpoint. Parks apply one FX per idle tick, so this is a
|
||||
// state a park genuinely sits in — and while it holds, the cancel rule
|
||||
// below is unsafe in both directions: only the restore's own ops can put
|
||||
// back what a half-finished park offlined, and a park arriving on that
|
||||
// restore must RESUME rather than annihilate or it strands every FX the
|
||||
// first pass had not reached. Restores carry no such state: they are
|
||||
// detached whole and applied in one drain.
|
||||
// Set once a park write has landed on this track — the live chain then
|
||||
// matches NEITHER endpoint, so the cancel-on-inverse rule below is unsafe
|
||||
// until the intent resumes/supersedes instead of annihilating. Restores
|
||||
// carry no such state: they are detached whole and applied in one drain.
|
||||
// Full contract: src/shell/view/CLAUDE.md's `view_fx_park` entry.
|
||||
bool partial = false;
|
||||
std::vector<FxOfflineOp> restoreOps;
|
||||
};
|
||||
@@ -158,22 +164,16 @@ struct PreParkFx {
|
||||
|
||||
// The FX half a fresh pre-park snapshot must carry when the park CANCELLED a
|
||||
// pending restore: those ops are the only surviving record of the pre-park
|
||||
// state, because the chain still reads the parked values until that restore
|
||||
// drains — and it never will, the cancel dropped it. Empty in (nothing was
|
||||
// cancelled) means the caller reads the live chain instead. The restore's own
|
||||
// keying travels with it so a slot-keyed snapshot lifted from a legacy
|
||||
// view_state does not silently become an identity-keyed one with no identities.
|
||||
//
|
||||
// FAITHFUL TO THE SNAPSHOT, NOT THE CHAIN. The ops describe the chain as it was
|
||||
// at the ORIGINAL park; applyPark enumerates it again at drain time. So an FX
|
||||
// added while the track was parked (a floating FX-chain window, ReaScript) is
|
||||
// absent from this reconstruction yet IS offlined by the cancelling park's
|
||||
// drain — and so never comes back online. Rare, and recoverable by hand in the
|
||||
// FX chain, but specific to the deferral.
|
||||
//
|
||||
// `offline` is already boolean by the time it arrives: makeRestorePlan narrowed
|
||||
// FxOfflineState's defensive int to FxOfflineOp's bool, so this widening back to
|
||||
// int restores the type, not lost information.
|
||||
// state, because the chain still reads the parked values and the cancel means
|
||||
// no drain will ever put them back. Empty (nothing cancelled) means the caller
|
||||
// reads the live chain instead; the restore's own keying travels with it so a
|
||||
// slot-keyed snapshot does not silently become identity-keyed with no
|
||||
// identities. The ops describe the chain as of the ORIGINAL park, not the live
|
||||
// chain applyPark re-enumerates at drain time — so an FX added while parked (a
|
||||
// floating FX-chain window, ReaScript) is absent here yet still offlined by the
|
||||
// cancelling park's drain, and never comes back online. Rare and hand-recoverable.
|
||||
// `offline` widens back from FxOfflineOp's bool to FxOfflineState's defensive int
|
||||
// — restoring the type, not adding information.
|
||||
inline PreParkFx preParkFxFromCancelledRestore(const std::vector<FxOfflineOp>& cancelled) {
|
||||
PreParkFx out;
|
||||
if (cancelled.empty()) return out;
|
||||
@@ -202,33 +202,27 @@ void deferFxRestore(ReaProject* proj, const std::string& guid, std::vector<FxOff
|
||||
// idle, and both early-out re-entrantly: one drain at a time, the outer one owns
|
||||
// the queue.
|
||||
//
|
||||
// RESTORES, applied whole, synchronously. Called on the idle tick AND before the
|
||||
// view model is serialized (persistViewState, render_in_place). The second call
|
||||
// is not an optimization: between a restore's synchronous flag writes and its
|
||||
// drain the FX are still offline while the model has already dropped the
|
||||
// snapshot that would replan them, so a save inside that window — deterministic
|
||||
// under a custom action chain like "toggle mode; save project" — records offline
|
||||
// FX beside a snapshot-free model, and nothing on reopen brings them back
|
||||
// online. The cost is that an action-driven switch pays the RELOAD hitch before
|
||||
// it returns; its repaint and undo block have both closed by then, which is what
|
||||
// the deferral was for.
|
||||
// RESTORES, applied whole, synchronously. Called on the idle tick and forced by
|
||||
// a subset of the paths that serialize the view model. WHICH paths force it, and
|
||||
// why forcing is required, is src/shell/view/CLAUDE.md's Documented-caveat entry
|
||||
// — kept there only, not restated here, so the two cannot drift apart.
|
||||
void drainDeferredFxRestores();
|
||||
|
||||
// PARKS, one FX per idle tick behind a short coalescing delay. Nothing waits on
|
||||
// a park, and unlike a restore it is safe to persist over: the flags are already
|
||||
// parked and the snapshot already stored, so a .rpp saved mid-window reopens with
|
||||
// the FX online, applyMode replans a park for that inactive leaf, and the drain
|
||||
// offlines it. It converges on its own, which is why this half does not force.
|
||||
// PARKS, one FX per idle tick behind a short coalescing delay (parkReadyAt /
|
||||
// parkIsReady above). Unlike a restore it is safe to persist over — it
|
||||
// converges on its own; see CLAUDE.md for why, and for the CPU-reclaim-is-
|
||||
// progressive consequence.
|
||||
//
|
||||
// One FX, not one track: a single convolution reverb or loaded sampler is the
|
||||
// unit of cost, so per-track chunking would not bound the hitch.
|
||||
// unit of cost, so per-track chunking would not bound the hitch — only the
|
||||
// per-FX split does, and only to ONE FX's unload per tick, not to zero.
|
||||
//
|
||||
// The delay's cost is the width of a documented hazard, not a new one. applyPark
|
||||
// enumerates the chain LIVE at drain time while the pre-park snapshot was taken
|
||||
// at switch time, so an FX added inside the window is offlined carrying no
|
||||
// snapshot entry — resolveFxRestore then has no op for it and it stays offline.
|
||||
// Same failure as the cancelled-restore case below, over a longer window; keep
|
||||
// the delay short.
|
||||
// The live-enumeration hazard's width is delay + (FX count × tick interval), NOT
|
||||
// the delay alone: firstOnlineFx re-enumerates the chain LIVE every tick, not
|
||||
// just the first, so an FX added at any point before the park retires is
|
||||
// offlined carrying no snapshot entry — resolveFxRestore then has no op for it
|
||||
// and it stays offline. Same failure as the cancelled-restore case above, over a
|
||||
// longer window. Do not lengthen the delay casually; it is only one term.
|
||||
void tickDeferredFxParks();
|
||||
|
||||
// Drops every pending intent without applying it. Called when the model the
|
||||
|
||||
Reference in New Issue
Block a user