fix: a still frame's measurements are the frame's, not the viewport's
A measurement is a statistic of the window the pane is *about*, and on a pinned view -- a held event, a paused stream, an opened file -- that is the frame, not the part of it on screen. The pane followed the viewport instead, so a wheel on a latched event moved `average` and `top`: the reported "when measuring a still frame, moving the zoom makes measurements such as average or top change". Nothing about the data had changed; the user was only looking closer. The window is `RowSet::pinned_range()` while the pin frames the view and the viewport otherwise, and the choice is made in `update_measures()` -- the call site -- rather than inside `MeasureWindowReader`, because it is *booked* as well as read: that window is what the "it moved" test compares against `App::measure_window`, so a gesture inside the pin spends no recompute either, and a reader that chose its own would leave a pinned view recomputing every gesture frame for a number that cannot change. `pinned_range()` is now the one expression of the pin's extent, shared with the framing (`hold_pinned_window()`, `fit_x()`), and the reader is handed the window instead of reading the plot. Live is unchanged: there the window really moves and the statistic follows it, and the accumulator's rule about what resets the history is about neither. `UDPSCOPE_PROBE_MEASUREPIN_MS` is the guard, three legs of before/after a gesture on the row's own axis. On a latched event zoomed to a quarter (`view=[-1..1]` -> `[-0.25..0.25]`, `pin` unchanged): `same=4/4 for_same=1 pass_delta=0`, every value bit-identical -- `average=0.000000 top=0.957975 peak to peak=2.000000 amplitude=1.915950` both sides. The same on a pause taken from it. Since the key `for=` follows `pin=` now, `[measure-trace]` gained the field, so a held run reads `for==pin` with `swin` free to sit inside it. On a live view a zoom to an eighth reads `same=0/4 for_same=0 pass_delta=3 span_after=0.125000 follows=1` with the values moving -- the case the rule must not swallow. Before the fix the same probe reads `same=1/4 for_same=0 pass_delta=1 span_after=0.500000` on the held event (`top` `0.957975` -> `0.957966`) and `same=0/4 span_after=0.125000` on the pause (`top` `0.957944` -> `0.358614`). Live reads the same either way. Also fixed, found by the pause leg: pausing while an event is *held* froze an **empty** picture. `on_pause_toggled()` read `stream_at_x0()` after setting the pause state, and that accessor follows the display -- `paused` with no pause view published yet answers `-display_epoch()`, which on a held event is 0, so the cut searched the ring at edge-relative x, found nothing, and the frame bound an empty snapshot: every measurement `--`, every ruler gone (`ev=650001` on the probe's line now, `ev=0` before). The offset and the window are read first, which is the offset the axis is actually displaying. The live case is unaffected, which is why the pause probe, which pauses a live view, never saw it. Docs corrected where they asserted the old half-truth: the invariants now separate the accumulator's rule (unchanged) from the window a value is computed over (this change), the trigger/pinning file names the pin as load-bearing for both the framing and the numbers and records the pause ordering rule, `verification.md`/`probe-readings.md` describe the new probe and `pin=`, and the README and the manual say what a still frame measures. Checked: `cmake --build build --clean-first -j4` 0 errors and the documented three warnings; `make -C build test-ui` 13 ok; `make -C build test-help` green; an exit-dump run 0 Gtk-WARNING/Gtk-CRITICAL and `gtk warnings: 0`; `[measurepin-probe]`, `[pan-probe]` (`held=1`, `allowed=1`, `limited=1`), `[pause-probe]` (`event_size` non-zero exactly while paused, `axis` inside `frozen_x`, `skips` 0 live / 30 paused), `[stats-probe]` (`dn=1` across the pause), `[zoom-probe]` (`gap_px=0.0` at every step) and `[measure-probe]` + the dump's `measures window:` (all-types run `marks_in_window=4/4`, `pin=off` live) on the fast mock. `measure cost:` is unchanged in kind: `raw=37298 (0.0%)` of `covered=121704537`, 1.04 ms a pass -- the runs that read higher are the probe's own zoomed-to-a-slice windows, where the window is legitimately shorter than a bucket.
This commit is contained in:
1 parent
d91957a2fe
commit
0a209becb1
13 files changed
+719
-114
No files matched your search
@@ -20,17 +20,38 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
own -- `top`/`bottom`/`amplitude` take a second read of that same **bucket** list and never of
|
||||
the window (the flat-level invariant below). **Adding a type must not add a pass** -- that is
|
||||
what `compute_measure()`'s `stats` argument is for.
|
||||
**The "it moved" test is asked in every mode, a held event included.** Suppressing it while
|
||||
held (`moved = ... && !held`) is the shape the pinned-window fix first took, and it is wrong
|
||||
for the one mode where a *gesture* is the only thing that can move the window: a latched event
|
||||
zoomed forty times over left the pane describing the whole 2 s event with `passes` frozen,
|
||||
which is the reported "measurements move when zooming or panning on data" -- the numbers stand
|
||||
still while the data under them moves. Nothing is needed for the pinned case anyway, because a
|
||||
window that is merely re-pinned satisfies `swin == measure_window` exactly and fails the move
|
||||
test 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. The held/live state is still a recompute of its own, so a mode
|
||||
change is not missed.
|
||||
**The window a value is computed over is the *pin* while the view is pinned, and the viewport
|
||||
otherwise.** A pinned view -- a held event, a paused stream, an opened file -- is a *still
|
||||
frame*: the data behind it does not change, so a pan or a zoom only looks closer. Measuring the
|
||||
pin (`RowSet::pinned_range()`, the same accessor `hold_pinned_window()` clamps into, so "what
|
||||
frames this view" has one expression) is what makes a gesture change **no value at all** --
|
||||
and it is decided in `update_measures()`, at the call site, rather than inside
|
||||
`MeasureWindowReader`, because the decision is also *booked*: the window
|
||||
`MeasureWindowReader` is handed is the same one the "it moved" test compares against
|
||||
`App::measure_window`, so a gesture inside the pin spends no recompute either. A reader that
|
||||
chose its own window would leave a pinned view recomputing on every gesture frame for a number
|
||||
that cannot change (457 passes in one run is the shape that mistake took).
|
||||
This test has been wrong in both directions and the reasoning is worth keeping. Suppressing it
|
||||
while held (`moved = ... && !held`) left the pane describing the whole 2 s event
|
||||
(`for=[0.47..2.47]`) while a 50 ms slice of it was on screen; *removing* the exemption was the
|
||||
opposite mistake, and it is the reported "when measuring a still frame (a triggered event, or a
|
||||
paused stream) moving the zoom makes measurements such as average or top change, which should
|
||||
never happen". The two are one question asked of two different windows: **which window is this
|
||||
pane about** (the pin, while pinned) and **has it moved** (asked of that window in every mode).
|
||||
Answers: a pinned view still recomputes when it should -- a fresh latch, a re-stated pin, a new
|
||||
snapshot, the held/live change -- and a settled one satisfies `swin == measure_window` exactly
|
||||
and so reads `moved == false` on its own.
|
||||
`[measurepin-probe]` is the reading: on a latched event zoomed to a quarter of itself,
|
||||
`same=4/4 for_same=1 pass_delta=0` with the values bit-identical and `for=` still at the pin;
|
||||
the same on a pause taken from a held event. Live is the case that must **not** be swallowed,
|
||||
and it reads `same=0/4 for_same=0` with a positive `pass_delta`, `span_before=2.000000`,
|
||||
`span_after=0.125000` and `follows=1` -- the results window narrowed with the viewport and the
|
||||
values moved with it. The
|
||||
one path that is *not* this rule is the window control: `apply_time_window()` unpins
|
||||
(`set_time_window()` clears `pinned_`), so for the frames before the held branch re-states the
|
||||
pin the view really is live-framed and the pane measures the viewport -- `[measure-trace]`
|
||||
prints `pin=off` for exactly those frames, which is what makes them legible rather than looking
|
||||
like the defect again.
|
||||
The marks' *ink* is set where they are created (`install()` in `plot_host.cpp`): 0.75 px and
|
||||
dashed 4/4 through `Plot::set_cursor_stroke()`, which is per cursor because the trigger marker
|
||||
(also a cursor) keeps the solid `line_width_px` stroke. A new measure mark must take that
|
||||
@@ -268,6 +289,10 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
changed".** An explicit Reset, an add/edit/delete (all three funnel through
|
||||
`rebuild_measures_pane()`), a CONFIG, and a change of display window each change the
|
||||
*question*, so the history is cleared. A pause, a resume, a trigger latch, a pan and a zoom do
|
||||
not: they change which samples answer it, which is precisely what averaging over many windows
|
||||
is for. The pane-wide toggle does not reset either — it only shows and hides labels that are
|
||||
always created and always fed, so reading the plot for a moment does not cost the history.
|
||||
not: on a **live** view they change which samples answer it, which is precisely what averaging
|
||||
over many windows is for -- and on a **pinned** one they change *nothing*, not even a value
|
||||
(the window invariant above), so there is nothing for the history to see either way. The rule
|
||||
is about the *accumulator* only; it is not a claim that a gesture leaves a value alone, which is
|
||||
a property of the window a value is computed over and is the other half of this change. The
|
||||
pane-wide toggle does not reset either — it only shows and hides labels that are always created
|
||||
and always fed, so reading the plot for a moment does not cost the history.
|
||||
@@ -81,13 +81,20 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
the far threshold of the side it is leaving. Seeding the previous side from `0` -- the undecided code --
|
||||
fires on a signal that starts *inside* the band and leaves it without ever reaching the level, an event the
|
||||
trigger is not supposed to see.
|
||||
- **A pinned window is *clamped* into the event every frame, and a zoom in is allowed.**
|
||||
**A pinned window is *clamped* into the event every frame, and a zoom in is allowed.**
|
||||
While an event is held, a capture is filling, or the view is paused, `tick()` calls
|
||||
`RowSet::hold_pinned_window()`, which caps the span at `[-pinned_pre_, +pinned_post_]`'s and
|
||||
clamps the position into it (and states `Off`, since a following group would re-derive from a
|
||||
live edge). **A zoom in must keep working** -- "not outside the event" is not "not readable",
|
||||
and this function's first version *re-stated* the pin every frame, which made the wheel a no-op
|
||||
in single trigger mode: an acquired event could not be examined at all.
|
||||
**What a gesture inside the pin may *not* move is a measurement**, and it is not this function
|
||||
that holds that: `pinned_range()` is the one expression of the pin's extent, and
|
||||
`update_measures()` measures *it* rather than the viewport while the view is pinned, so a pan
|
||||
or a zoom on a still frame changes no value and spends no recompute (see
|
||||
[invariants-measurements.md](invariants-measurements.md); `[measurepin-probe]` is the reading,
|
||||
`pin=`/`for=` in `[measure-trace]` the field to watch). The pin is therefore load-bearing twice
|
||||
over, and a change to what `pinned_range()` says changes both the framing and the numbers.
|
||||
**The request is read from the row's own axis, not from the group.** A member's `set_x_range`
|
||||
-- what the controller writes for a gesture -- is *not* adopted into the group while the follow
|
||||
is off, so a clamp that read the group only ever saw its own previous answer (the probe showed
|
||||
@@ -148,6 +155,18 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
||||
afterwards if the probe zoomed in. Before the fix the two disagreed outright:
|
||||
`axis=[-1.44..0.56]` over data that started at `0.0`. (The event pin already worked this way; the
|
||||
pause now agrees.)
|
||||
- **The pause reads the display offset *before* it sets the pause state, and a pause taken on a
|
||||
held event depends on that order.** `on_pause_toggled()` reads the row's axis and
|
||||
`stream_at_x0()`, cuts `view + offset` out of the ring, and hands the same offset to the
|
||||
snapshot as its `x_offset`. `stream_at_x0()` follows the *display*: the moment `paused` is true
|
||||
with no pause view published yet it answers `-display_epoch()`, which on a `Held` view is **0**
|
||||
-- so the cut searched the ring at the event's edge-relative x, found nothing, and froze an
|
||||
**empty** picture: `ev=0` on the probe's line, every measurement `--` and every ruler gone,
|
||||
which is a much louder symptom than the empty margin above and reaches a user the same way
|
||||
(pause while an event is on screen). Read first, it is the offset the axis is actually
|
||||
displaying, which is what the whole scheme requires. The live case is unchanged -- there the
|
||||
epoch does not depend on `paused()`, which is why the pause probe, which pauses a *live* view,
|
||||
never saw it.
|
||||
- **The event axis is pinned from the snapshot's own stamps, not from the request.** A
|
||||
capture may legitimately be shorter than asked for — the ring need not reach back a
|
||||
full pre-trigger share on the first latch after arming, so `snap=[-0.339..1.000]`
|
||||
|
||||
@@ -253,9 +253,10 @@ Two lines of the self-check output are assertions in disguise:
|
||||
seconds, and the rest the channel's unit. `switch_on` and `reset_sensitive` are the panel's *frame* read back: the switch widget against
|
||||
`measure_stats_on`, which the code drives, and the Reset button that follows it. Either disagreeing with
|
||||
`[stats-probe] on=` is a `.ui` that lost the widget, or lost the wiring to it; `pane_rows=R` is what
|
||||
proves the readout rows landed in the frame's `measures_list` rather than nowhere. `marks_shown` counts two per shown measurement
|
||||
(a min/max showing both its marks counts two of its own). `measures window:` is the
|
||||
timeliness assertion -- the window the numbers were computed over against the one on screen.
|
||||
`marks_shown` counts two per shown measurement
|
||||
(a min/max showing both marks counts two of its own). `measures window:` is the
|
||||
timeliness assertion -- the window the numbers were computed over against the one that *should*
|
||||
be on screen.
|
||||
**The reading that must hold is `marks_in_window=K/K`**: every *time* mark inside the window
|
||||
on screen. Both ranges are 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
|
||||
@@ -263,15 +264,22 @@ Two lines of the self-check output are assertions in disguise:
|
||||
(`App::measure_view_window`) and is deliberately not read back here: the dump runs from the
|
||||
destroy handler, by which point the exit probe has switched the trigger on, and
|
||||
`stream_at_x0()` then answers for a mode no frame was ever drawn in -- which read as a 45000 %
|
||||
drift on a run whose every frame was exact. And the count is against `win`, not against
|
||||
`measure_window`, which contains every mark by construction: read that way it was a tautology
|
||||
drift on a run whose every frame was exact. `pin` is the same tick's pin (`App::measure_pin_window`,
|
||||
`off` when nothing was pinned) and it is the **target** while the view is pinned: a measurement
|
||||
on a still frame describes the frame, so `drift` there is `results_for` against the *pin*, and a
|
||||
zoom that leaves the screen showing a slice of that frame is not drift. Read against `win` -- the
|
||||
old rule -- a zoomed held event would report the intended behaviour as the defect it was once
|
||||
reported as. The count is still against `win`, the window **on screen**, not against
|
||||
`measure_window`/`pin`, which contain every mark by construction: read that way it was a tautology
|
||||
that printed `2/2` while the pane described a window a forty-fold zoom away from the one
|
||||
displayed. On a window **shorter than the refresh interval** the count rides the same sawtooth
|
||||
displayed. A *time* mark may legitimately sit outside `win` on a pinned view that has been zoomed
|
||||
into the pin (the mark is a position in the window the result describes, and that window is the
|
||||
frame), so the strict `K/K` reading belongs to a live view and to a pinned view left at its pin.
|
||||
On a window **shorter than the refresh interval** the count rides the same sawtooth
|
||||
`drift` does -- the 50 ms window the zoom sweep leaves behind can lag far enough that *every*
|
||||
time mark is off screen, so `0/K` there is read beside `drift` and not alone. The strict
|
||||
reading belongs to the documented 2 s run, and to a **held** window in any run: a latched
|
||||
event does not scroll, so `[measure-trace]` must show `for=` equal to `swin=` exactly for as
|
||||
long as the mode stays `held`, zoom and pan included.
|
||||
time mark is off screen, so `0/K` there is read beside `drift` and not alone. `0/0` is a pane
|
||||
whose types carry no time marks at all (`average`, `top`, `amplitude` and `peak to peak` draw
|
||||
rulers only), and is not a reading about marks.
|
||||
`drift` itself is a **single sample of a sawtooth** and is not an assertion on its
|
||||
own: a live view scrolls between recomputes, so it rises to one `kMeasureRefreshUs` of scroll
|
||||
and drops to zero at each pass. The figure is `(|Δmin| + |Δmax|) / span`, so a pure scroll
|
||||
@@ -284,9 +292,11 @@ Two lines of the self-check output are assertions in disguise:
|
||||
is a dump that landed just after a pass, not a better state. Read it against the recompute count:
|
||||
*more* recomputes with a *higher* drift sample is the sawtooth, while a drift that exceeds one
|
||||
refresh interval of scroll, or marks outside the window, is the defect. A measurement is
|
||||
recomputed when the window moves (2 % of its span) or the held/live state changes, never on a
|
||||
wall-clock timer: a timer is what let a marker sit outside the viewport and a dragged one
|
||||
survive a frame. **`marks_selectable` must be 0**: the
|
||||
recomputed when the window **it describes** moves (2 % of its span) or the held/live state
|
||||
changes, never on a wall-clock timer: a timer is what let a marker sit outside the viewport and a
|
||||
dragged one survive a frame, and it is the window it describes -- the viewport live, the pin while
|
||||
pinned -- so that a gesture on a still frame spends no recompute at all. **`marks_selectable`
|
||||
must be 0**: the
|
||||
measurement marks are created unselectable (`Plot::set_cursor_selectable()`, which the
|
||||
library gained for this), read back out of the plot here -- re-placing a marker every frame
|
||||
does **not** stop a drag, because the controller holds it for the gesture's life and the
|
||||
@@ -1152,6 +1162,58 @@ Four line kinds, and the reading is the third:
|
||||
most and `0.986495` on the rest. **A line's `--` on a *lagging* trace is the trace's, not the
|
||||
reading's** — the pane is refreshed on a cadence, so quote an earlier line beside its own `view=`.
|
||||
|
||||
## `[measurepin-probe]`
|
||||
|
||||
`UDPSCOPE_PROBE_MEASUREPIN_MS`. The reading for **what window a measurement describes**: on a still
|
||||
frame the pane is about the *frame*, so a pan or a zoom changes no number, and on a live view the
|
||||
numbers still follow the window. It installs `measure_probe`'s own four types on the first *channel*
|
||||
(`average`, `top`, `peak to peak`, `amplitude`), arms the trigger in **single** mode, and runs three
|
||||
legs — each one reading the pane before and after a **gesture** written on the row's own axis, which
|
||||
is what a drag, a wheel or a box zoom writes. Nothing here takes the window-control path: that one
|
||||
*calls off* the pin (`set_time_window()` clears `pinned_`), so measuring it would test a different
|
||||
question.
|
||||
|
||||
Run it against the fast mock, and mind the datagram limit: `--batch 2000` is 80 kB of payload for the
|
||||
mock's five signals and dies with `OSError: [Errno 90] Message too long`, so the command here is
|
||||
`--rate 1300000 --batch 1500 --freq 1`. Its sequence is about 12 s: it waits for a latch, then
|
||||
~1.5 s per leg, so `UDPSCOPE_EXIT_AFTER_MS=20000` covers it with room.
|
||||
|
||||
```
|
||||
[measurepin-probe] 4 measurements on 'sine', trigger single
|
||||
[measurepin-probe] wait mode=waiting pinned=0 at_pin=0 for=[-1.419616..0.580384] pin=[0.000000..1.000000] passes=14
|
||||
[measurepin-probe] wait mode=capturing pinned=1 at_pin=0 for=[-1.419616..0.580384] pin=[0.000000..1.000000] passes=14
|
||||
[measurepin-probe] wait mode=capturing pinned=1 at_pin=1 for=[0.881155..2.881155] pin=[0.881155..2.881155] passes=15
|
||||
[measurepin-probe] wait mode=held pinned=1 at_pin=1 for=[0.881155..2.881155] pin=[0.881155..2.881155] passes=15
|
||||
[measurepin-probe] held/before pinned=1 mode=held view=[-1.000000..1.000000] pin=[-1.000000..1.000000] for=[0.881155..2.881155] passes=16 ev=2600001 evx=[-1.000000..1.000000] average=0.000000 top=0.957975 peak to peak=2.000000 amplitude=1.915950
|
||||
[measurepin-probe] held/after pinned=1 mode=held view=[-0.250000..0.250000] pin=[-1.000000..1.000000] for=[0.881155..2.881155] passes=16 ev=2600001 evx=[-1.000000..1.000000] average=0.000000 top=0.957975 peak to peak=2.000000 amplitude=1.915950 same=4/4 for_same=1 pass_delta=0 span_before=2.000000 span_after=2.000000 follows=0
|
||||
[measurepin-probe] paused/before pinned=1 mode=paused view=[-0.250000..0.250000] pin=[-0.250000..0.250000] for=[1.631155..2.131155] passes=17 ev=650001 evx=[-0.250000..0.250000] average=0.000003 top=0.957944 peak to peak=2.000000 amplitude=1.915906
|
||||
[measurepin-probe] paused/after pinned=1 mode=paused view=[-0.062500..0.062500] pin=[-0.250000..0.250000] for=[1.631155..2.131155] passes=17 ev=650001 evx=[-0.250000..0.250000] average=0.000003 top=0.957944 peak to peak=2.000000 amplitude=1.915906 same=4/4 for_same=1 pass_delta=0 span_before=0.500000 span_after=0.500000 follows=0
|
||||
[measurepin-probe] live/before pinned=0 mode=live view=[-2.000000..0.000000] pin=[off] for=[1.666922..3.666922] passes=22 ev=0 evx=[0.000000..0.000000] average=-0.000000 top=0.957975 peak to peak=2.000000 amplitude=1.915948
|
||||
[measurepin-probe] live/after pinned=0 mode=live view=[-1.062500..-0.937500] pin=[off] for=[2.746345..2.871345] passes=24 ev=0 evx=[0.000000..0.000000] average=-0.427665 top=-0.083224 peak to peak=0.687725 amplitude=0.644726 same=0/4 for_same=0 pass_delta=2 span_before=2.000000 span_after=0.125000 follows=1
|
||||
```
|
||||
|
||||
- `view`/`pin`/`for`/`passes`/`ev`/`evx` are the state: the axis, the pin's own extent, the window
|
||||
the printed values were computed over, the pane's pass count, and the **bound event series'** own
|
||||
sample count and extent — the frame the numbers are about. `ev=0` while a view is pinned is an
|
||||
*empty* frozen frame (every value `--`), which is the reading that named the pause ordering defect
|
||||
(`on_pause_toggled()` read its display offset after setting the pause state).
|
||||
- `same=K/4` — the values that are **bit-identical** before and after the gesture. On a pinned view
|
||||
it must be `4/4` with `for_same=1` and `pass_delta=0`: the frame's samples did not change, so
|
||||
neither may a number, and the pane may not even spend a recompute on the gesture. `for_same` is
|
||||
the app's own 2 %-of-span criterion (the rule that decides a recompute is not owed), not equality:
|
||||
the pin is re-derived from the snapshot's stamps every tick and the pane keeps the window its last
|
||||
pass used, so the two differ in the last digits without anything being owed.
|
||||
- `follows` — the live leg's assertion, and the case the still-frame rule must *not* swallow:
|
||||
`for_same=0`, a positive `pass_delta` and the results window's span narrowed from the axis'
|
||||
2.000000 to 0.125000, with `average`, `top` and `peak to peak` all moving. `follows=1` with `same=4/4` on a
|
||||
pinned leg, or `follows=1` while pinned at all, is the defect in one direction or the other.
|
||||
- The `before`/`after` pair is taken only once the pinned frame has **settled** (a `wait` line per
|
||||
*change* of the state being waited on until then, then nothing): the frames that bind a snapshot or freeze a pause re-state the
|
||||
pin from the request and then from the snapshot's own stamps, and a gesture made in one of those
|
||||
frames is reverted by that re-statement — the `[zoom-probe]`'s own gesture sweep loses its first
|
||||
two steps to it, which is visible in its `ges/x1`, `ges/x2` lines. A `gave up waiting after N
|
||||
polls` line means no latch (or no pin) was ever reached, and is a reading about the stream.
|
||||
|
||||
## `[search-probe]`
|
||||
|
||||
`UDPSCOPE_PROBE_SEARCH_MS`. The handler calls `condition_probe()` directly — the condition is
|
||||
|
||||
+34
-14
@@ -96,20 +96,25 @@ precedent, not an exception.
|
||||
overshoot, kept by the extremes and excluded by the levels. The per-type values, the reason for
|
||||
each, and the cost of the histogram are in
|
||||
[invariants-measurements.md](invariants-measurements.md).
|
||||
`[measure-trace] mode=M view=[..] s0=S swin=[..] for=[..] passes=N <type>=<value>... marks=N
|
||||
level:V/time:V...` is a repeating line (250 ms) from the probe: the pane's numbers beside the
|
||||
*current* window and the marks actually placed, which is the only way a stale result or a mark
|
||||
that should not be drawn shows up as a number. `swin` is `view` moved onto the stream's clock by
|
||||
`s0` (`stream_at_x0()`), and `for` is the window the printed numbers were computed over, in the
|
||||
same clock -- so a reading that moved while `view` stood still is a change of `s0` (a new
|
||||
snapshot under the same mode) and not a stale pass, and only these three fields tell the two
|
||||
apart. `passes` beside them says whether a recompute answered the move. **While `mode=held`,
|
||||
`for` must equal `swin` exactly**, through any number of zooms and pans: a latched event does
|
||||
not scroll, so there is nothing for a recompute to be late for. That reading is where
|
||||
the measurements standing still under a gesture was found -- `for=[0.47..2.47]` against a
|
||||
`swin=[1.417..1.467]` with `passes` frozen, the pane describing the whole 2 s event while a 50 ms
|
||||
slice of it was on screen. The line must also never print `marks=0` while measurements with
|
||||
`show_on_plot` are on and the data is there -- that was the rulers blinking off at a mode change.
|
||||
`[measure-trace] mode=M view=[..] s0=S swin=[..] pin=[..|off] for=[..] passes=N
|
||||
<type>=<value>... marks=N level:V/time:V...` is a repeating line (250 ms) from the probe: the
|
||||
pane's numbers beside the *current* window and the marks actually placed, which is the only way a
|
||||
stale result or a mark that should not be drawn shows up as a number. `swin` is `view` moved onto
|
||||
the stream's clock by `s0` (`stream_at_x0()`), `pin` is the pin's own extent in that clock (`off`
|
||||
when nothing is pinned) and `for` is the window the printed numbers were computed over -- so a
|
||||
reading that moved while `view` stood still is a change of `s0` (a new snapshot under the same
|
||||
mode) and not a stale pass, and only these fields tell those apart. `passes` beside them says
|
||||
whether a recompute answered the move. **While the view is pinned, `for` must equal `pin` -- and
|
||||
`swin` is free to sit anywhere inside it** -- through any number of zooms and pans: a latched
|
||||
event's numbers are the *frame's*, so a gesture inside the pin changes neither them nor `passes`.
|
||||
Read the other way round it is the defect: `for` following `swin` into the zoom while the values
|
||||
move is the reported "on a still frame the measurements change when I zoom". Neither reading is
|
||||
the old "measurements stand still while the data moves", which was `for=[0.47..2.47]` against a
|
||||
`swin=[1.417..1.467]` with `passes` frozen -- the pane describing a window the plot had left.
|
||||
The line must also never print `marks=0` while measurements with `show_on_plot` are on and the
|
||||
data is there -- that was the rulers blinking off at a mode change. `pin=off` while the mode is
|
||||
`held` is not a stale reading but the window control: `apply_time_window()` unpins, and for those
|
||||
frames the view is live-framed and measuring the viewport is right.
|
||||
`measure cost: N recomputes, X ms total, Y ms each, data D ms (P%), covered=C buckets=B
|
||||
raw=R (F%), N ns/covered sample` follows it: **read it against a run with
|
||||
`UDPSCOPE_PROBE_MEASURE_MS` unset**, because a frame mean cannot attribute a cost. The last
|
||||
@@ -168,6 +173,21 @@ precedent, not an exception.
|
||||
`--square-harmonics 30` for the `0.986496` and the `0.302744`, `--square-harmonics 0` for the
|
||||
`1.000000` and the empty band.
|
||||
|
||||
- `[measurepin-probe]` (stderr, `UDPSCOPE_PROBE_MEASUREPIN_MS`) — **what window a measurement
|
||||
describes**: on a still frame the pane is about the *frame*, so a pan or a zoom changes no
|
||||
number, and on a live view the numbers still follow the window. Three legs — a latched event, a
|
||||
pause taken from it, and a live view after the trigger is switched off — each read the pane
|
||||
before and after a *gesture* written on the row's own axis (never the window control, which
|
||||
calls off the pin). The assertions, in order: the pinned legs read **`same=4/4 for_same=1
|
||||
pass_delta=0`** with `for=` still at the pin (the values bit-identical and no recompute spent on
|
||||
the gesture), and the live leg reads **`follows=1`** with `for_same=0`, `pass_delta>0` and the
|
||||
results window's span narrowed to the axis'. Its sequence is about 12 s, so
|
||||
`UDPSCOPE_EXIT_AFTER_MS=20000`; run it on the fast mock with a batch the datagram limit allows
|
||||
(`--rate 1300000 --batch 1500 --freq 1`, **not** `--batch 2000`, which is 80 kB of payload and
|
||||
dies with `Message too long`). The lines, the fields and the settle wait are in
|
||||
[probe-readings.md](probe-readings.md); the window rule itself is in
|
||||
[invariants-measurements.md](invariants-measurements.md).
|
||||
|
||||
- `[pan-probe] asked=[..] was=[..]`, `[pan-probe] after=[..] held=D asked=[..]`, then
|
||||
`[pan-probe] zoom asked=[..] after=[..] span=S window=W limited=L`
|
||||
(stderr, `UDPSCOPE_PROBE_PAN_MS`) — **`held` must be 1**: a gesture must not move a window
|
||||
|
||||
Reference in new issue
Block a user