Persist the reclaimer's FX release so a reload cannot re-drive a spent record, and report the FX-restore drops it was swallowing
This commit is contained in:
@@ -10,12 +10,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "shell/persist/session.h" // saveViewStateOnly — the release's durable half
|
||||
#include "shell/view/view.h" // modeApplyInProgress
|
||||
#include "shell/view/view_park.h" // fxGuidString — the one FX-identity read
|
||||
#include "shell/view/view_tracks.h"
|
||||
|
||||
#define REAPERAPI_MINIMAL
|
||||
#define REAPERAPI_WANT_EnumProjects
|
||||
#define REAPERAPI_WANT_ShowConsoleMsg
|
||||
#define REAPERAPI_WANT_TrackFX_GetCount
|
||||
#define REAPERAPI_WANT_TrackFX_GetOffline
|
||||
#define REAPERAPI_WANT_TrackFX_SetOffline
|
||||
@@ -67,8 +69,8 @@ std::set<std::string> liveTrackGuids(ReaProject* proj) {
|
||||
|
||||
// The live half of drainTick's Host contract (see fx_reclaim.h).
|
||||
struct ReclaimHost {
|
||||
ReaProject* project = nullptr;
|
||||
ViewModeModel* model = nullptr;
|
||||
ReaProject* project = nullptr;
|
||||
ReaSamplerSession* session = nullptr;
|
||||
|
||||
// Monotonic and sub-millisecond: the budget below discriminates at 30ms, which
|
||||
// GetTickCount's ~16ms granularity cannot do. No wraparound to guard against, so
|
||||
@@ -111,10 +113,41 @@ struct ReclaimHost {
|
||||
}
|
||||
|
||||
// Reached only after the drain has unwound and the queue has proved its generation
|
||||
// did not move, so `model` is still the object the queue was derived against.
|
||||
void releaseFx(const std::string& guid) { model->releaseSnapshotFx(guid); }
|
||||
// did not move, so the session still holds the model the queue was derived against.
|
||||
void releaseFx(const std::string& guid) { session->view().releaseSnapshotFx(guid); }
|
||||
|
||||
// saveViewStateOnly writes the ACTIVE project and takes none, so re-read that rather
|
||||
// than trust the project this tick pinned: a write pumps, and a tab switch inside one
|
||||
// would otherwise land this model in the wrong project's ext state. Skipping is
|
||||
// self-healing — coming back to the tab reloads the model from the record still on
|
||||
// disk, and the re-derived online writes nothing and releases again. Promptless,
|
||||
// unlike design_view_actions' persistViewState, which can open a modal Save-As.
|
||||
void persistModel() {
|
||||
if (EnumProjects(-1, nullptr, 0) != project) return;
|
||||
session->saveViewStateOnly();
|
||||
}
|
||||
};
|
||||
|
||||
// The drop set the last console report named, and the project it named it for. Same
|
||||
// one-per-changed-set memo as view_park's reportExposedTracks, which owns the reasoning;
|
||||
// here the repeat it swallows is an aborted tick's re-derivation resolving identically.
|
||||
// The pointer is COMPARE-ONLY and never dereferenced.
|
||||
ReaProject* g_reportedDropOwner = nullptr;
|
||||
std::vector<TrackFxDrops> g_reportedDrops;
|
||||
|
||||
void reportDrops(ReaProject* proj, const std::vector<TrackFxDrops>& drops) {
|
||||
if (!shouldReportDrops(proj == g_reportedDropOwner, g_reportedDrops, drops)) return;
|
||||
g_reportedDropOwner = proj;
|
||||
g_reportedDrops = drops;
|
||||
|
||||
FxRestoreDrops total;
|
||||
for (const TrackFxDrops& d : drops) total.add(d.drops);
|
||||
const std::string msg = describeFxRestoreDrops(total, static_cast<int>(drops.size()));
|
||||
// "!SHOW:" so a background tick cannot force the console open
|
||||
// (reaper_plugin_functions.h:6536).
|
||||
if (!msg.empty()) ShowConsoleMsg(("!SHOW:" + msg).c_str());
|
||||
}
|
||||
|
||||
// Every path that stops scheduling ticks leaves the same state behind: no queue, no pin,
|
||||
// nothing pending. The debounce clock is NOT part of it — see fx_reclaim.h.
|
||||
void goIdle() {
|
||||
@@ -137,7 +170,7 @@ void invalidateFxReclaim() {
|
||||
|
||||
bool fxReclaimDrainInProgress() { return g_draining; }
|
||||
|
||||
void fxReclaimTick(ViewModeModel& model, ReaProject* proj) {
|
||||
void fxReclaimTick(ReaSamplerSession& session, ReaProject* proj) {
|
||||
if (g_draining) return;
|
||||
// The model is mid-rewrite and its records are being consumed out from under any
|
||||
// plan derived from it. The apply invalidates on its way out, so nothing is lost.
|
||||
@@ -161,12 +194,14 @@ void fxReclaimTick(ViewModeModel& model, ReaProject* proj) {
|
||||
// invalidated, so a REAPER quit mid-drain simply re-derives and finishes on reopen.
|
||||
if (g_queue.stale()) {
|
||||
g_project = project;
|
||||
const ViewModeModel& model = session.view();
|
||||
g_queue.adopt(makeQueue(
|
||||
view::desiredFxState(model, model.activeModeId(), liveTrackGuids(project))));
|
||||
}
|
||||
|
||||
ReclaimHost host{project, &model};
|
||||
ReclaimHost host{project, &session};
|
||||
const ReclaimRunResult run = g_queue.run(host);
|
||||
reportDrops(project, run.drops);
|
||||
if (!run.aborted && run.settled) goIdle();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user