fix: a recompute that renumbered its ring, and readouts frozen under a zoom
Three defects, all of them a window describing data that is not there. A math edit refilled the derived ring with `clear()`, which renumbers it from 0, leaving `produced()` short by the inputs' retained base. Since `live_window_locked()` clamps against every channel's `produced()`, that one lagging channel answered `n = 0` for the live view, every pause view and every trigger capture until a whole ring of fresh data had gone by -- the reported freeze, invisible until the ring first wraps, hence its intermittence. It now restarts with `clear_to(base)`, and `ChannelBuffer::republish_from(base)` puts the published window back: `acquire()` is incremental, so a rewrite in place published nothing and the pyramid went on describing the samples the edit replaced. The trigger's channel is an index, re-resolved by name after a renumbering -- but only in `rebuild_enabled_locked()`, so a math add or delete went through `set_math_channels()` with no restore, the index was clamped to -1, and `process()` returned at once while the panel still named the channel. The restore moves into `rebuild_channels()`, which *is* the renumbering, so no caller can forget it. And `update_measures()` suppressed its "the window moved" test while an event was held -- the one mode where a gesture is the only thing that can move it. Zooming a latched event left the pane describing the whole captured event. A pinned window needs no exemption: it lands on the range it already measured and fails the 2 % test by itself. The checks that missed all three: `UDPSCOPE_PROBE_MATHWRAP_MS` edits a graph whose ring has already wrapped, which `math_probe` structurally cannot reach; `[measure-trace]` now prints the mode, the window on screen and the window the numbers describe; and the dump's `marks_in_window` counts against the window on screen instead of against the one the marks were computed over, where it was a tautology. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
074cb611bc
commit
d91957a2fe
15 files changed
+374
-45
No files matched your search
+14
-5
@@ -5809,6 +5809,9 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
|
||||
if (const char* env = g_getenv("UDPSCOPE_PROBE_MATH_MS")) {
|
||||
g_timeout_add(static_cast<guint>(std::atoi(env)), math_probe, &app);
|
||||
}
|
||||
if (const char* env = g_getenv("UDPSCOPE_PROBE_MATHWRAP_MS")) {
|
||||
g_timeout_add(static_cast<guint>(std::atoi(env)), mathwrap_probe, &app);
|
||||
}
|
||||
if (const char* env = g_getenv("UDPSCOPE_PROBE_MEASURE_MS")) {
|
||||
g_timeout_add(static_cast<guint>(std::atoi(env)), measure_probe, &app);
|
||||
}
|
||||
@@ -6476,17 +6479,23 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
|
||||
// Both on the **stream's** clock, which is the space `measure_window` and the time marks
|
||||
// are stored in: a live axis is a fixed `[-window, 0]`, so comparing display x would read
|
||||
// no drift whatever the data did (see `App::measure_window`).
|
||||
const double s0 = model.stream_at_x0();
|
||||
const RowSet::Row& r0 = rows.row(0);
|
||||
const plot::Range view = r0.plot != nullptr ? r0.plot->x_range(r0.axis_x) : plot::Range{};
|
||||
const plot::Range win{view.min + s0, view.max + s0};
|
||||
//
|
||||
// `win` comes from the last **tick** (`App::measure_view_window`) and is not read back here:
|
||||
// this runs from the destroy handler, where the exit probe has already switched the trigger
|
||||
// on, and `stream_at_x0()` then answers for a mode no frame was drawn in.
|
||||
const plot::Range win = app.measure_view_window;
|
||||
const double span = app.measure_window.span();
|
||||
int in_window = 0;
|
||||
int marks = 0;
|
||||
// Against `win`, the window **on screen** -- not against `measure_window`, which is the
|
||||
// window the mark was computed over and so contains it by construction. Read that way the
|
||||
// count was a tautology: it printed `2/2` while the pane's numbers described a window 11 s
|
||||
// and a forty-fold zoom away from the one displayed, which is how a held event that never
|
||||
// recomputed under a gesture stayed invisible to every run of this check.
|
||||
for (const RowSet::MeasureMark& mk : app.measure_marks) {
|
||||
if (mk.level) continue;
|
||||
++marks;
|
||||
if (mk.value >= app.measure_window.min && mk.value <= app.measure_window.max) ++in_window;
|
||||
if (mk.value >= win.min && mk.value <= win.max) ++in_window;
|
||||
}
|
||||
// Whether the marks the app placed are *unselectable* -- the property the pointer's hit
|
||||
// test respects. Read back out of the plot, so this is the app's own setting and not
|
||||
|
||||
Reference in new issue
Block a user