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
+25
-3
@@ -284,8 +284,19 @@ void update_measures(App& app, bool force, bool accumulate) {
|
||||
const double s0 = app.model->stream_at_x0();
|
||||
const plot::Range view = r0.plot->x_range(r0.axis_x);
|
||||
const plot::Range swin{view.min + s0, view.max + s0};
|
||||
app.measure_view_window = swin;
|
||||
const bool held = app.model->display_mode() == DisplayMode::Held;
|
||||
const bool moved = app.measure_window_valid && !held &&
|
||||
// "It moved" is asked in **every** mode, a held event included. This test used to carry a
|
||||
// `&& !held`, and a held event is precisely the mode where a *gesture* is the only thing that
|
||||
// can move the window: with it, zooming a latched event forty times over left the pane
|
||||
// describing the whole 2 s event (`for=[0.47..2.47]` against a `swin=[1.417..1.467]`, `passes`
|
||||
// frozen), which is what "the measurements move when zooming or panning" was -- the numbers and
|
||||
// the time marks belonged to a window the plot had left. The term was aimed at a *pinned*
|
||||
// window recomputing every frame, and nothing is needed for that: a settled window satisfies
|
||||
// `swin == measure_window` exactly and so reads `moved == false` on its own. A pause is the
|
||||
// standing proof, since `display_mode()` reports `Paused` rather than `Held` for one and the
|
||||
// statistics probe already requires `n` to advance by at most one across a two-second pause.
|
||||
const bool moved = app.measure_window_valid &&
|
||||
std::fabs(swin.min - app.measure_window.min) +
|
||||
std::fabs(swin.max - app.measure_window.max) >
|
||||
0.02 * std::max(1.0e-12, swin.span());
|
||||
@@ -638,8 +649,19 @@ gboolean measure_trace(gpointer user) {
|
||||
RowSet::Row& r0 = a->rows->row(0);
|
||||
if (r0.plot == nullptr) return G_SOURCE_REMOVE;
|
||||
const plot::Range view = r0.plot->x_range(r0.axis_x);
|
||||
std::string line = "[measure-trace] view=[" + std::to_string(view.min) + ".." +
|
||||
std::to_string(view.max) + "]";
|
||||
// The window on screen *and* the window the numbers describe, both on the stream's clock, with
|
||||
// the offset that converts between them: a reading that moved without the view moving is a
|
||||
// change of `s0` (a new snapshot under the same mode), and only these three fields tell the two
|
||||
// apart. `passes` beside them says whether a recompute answered it.
|
||||
const double s0 = a->model->stream_at_x0();
|
||||
std::string line = "[measure-trace] mode=" +
|
||||
std::string(display_mode_name(a->model->display_mode())) + " view=[" +
|
||||
std::to_string(view.min) + ".." + std::to_string(view.max) + "] s0=" +
|
||||
std::to_string(s0) + " swin=[" + std::to_string(view.min + s0) + ".." +
|
||||
std::to_string(view.max + s0) + "] for=[" +
|
||||
std::to_string(a->measure_window.min) + ".." +
|
||||
std::to_string(a->measure_window.max) + "] passes=" +
|
||||
std::to_string(a->measure_passes);
|
||||
for (std::size_t i = 0; i < a->measures.size() && i < a->measure_results.size(); ++i) {
|
||||
const MeasureResult& r = a->measure_results[i];
|
||||
line += " " + std::string(measure_type_name(a->measures[i].type)) + "=" +
|
||||
|
||||
Reference in new issue
Block a user