fix: bound the density grid, and make [notrace] a reading instead of a flood

The plotter caps a density grid at kDensityCellCap now (deps/plotter), so a 2 s
window with persistence on no longer scales the image with the window: the
probe's four one-signal rows read 1 044 714 cells / 4 178 856 bytes where they
read 2 226 150 / 8 904 600, and a moved frame uploads 1 044 714 bytes where it
uploaded 2 226 150. The probe's own legs are unchanged (on peak=1.0000,
step-acquisitions peak=1.5000, step-time ratio=0.367879).

The paint probe no longer counts a frame the density is drawing as *traceless*.
`Renderer::draw_traces()` returns while the ink is the image, so every frame of
a persistence display stages no trace: the dump read `traceless paints: 653
(637 after the first bind)` for a run whose real figure is 4 (0), and the
`[notrace]` line printed for each of those frames. It is now behind
UDPSCOPE_PROBE_NOTRACE -- a per-paint line is a log flood and not a reading --
while the counters it details stay in every build, and the line stays reachable
for the state it was written for (a bound row that draws nothing).

Docs corrected where they stated the old rule: the README, the trigger
invariants, the density probe's readings, the verification list (the new
variable) and the manual page behind the panel's tooltip.
This commit is contained in:
Martino Ferrari committed 2026-10-16 21:09:11 +02:00
1 parent da826f2cb5
commit ac054a30c9
11 files changed
+131 -76

No files matched your search

+22 -16
View File
@@ -1271,8 +1271,10 @@ snapshots there and then, because a trigger that has stopped firing never reache
**Persistence: one image per signal, every acquisition.** The trigger panel's `Advanced` block
carries the *other* display of the same acquisitions, and it is not a count at all: `persistence`
turns on a **phosphor density** — the plotter's screen-sized intensity image (`Plot::set_density()`,
one `f32` cell per device pixel of the axes rect **per trace, in that trace's own signal colour**)
turns on a **phosphor density** — the plotter's intensity image (`Plot::set_density()`, the axes rect
in device pixels **per trace, in that trace's own signal colour**, bounded by the plotter's
`kDensityCellCap`: past **262 144 cells** — a mebibyte of counts — it scales the rect down uniformly
and magnifies the image back over it with a linear filter)
into which every triggered acquisition is deposited, the newest included, and which is drawn *in
place of* the traces. The current acquisition is therefore a sweep *inside* the image and fades with
the rest; no solid polyline is drawn over it, which is what a report of "the persistent image plus
@@ -1280,20 +1282,24 @@ the last acquisition, and the last one never fades in time mode" turned out to b
brightness is how many acquisitions passed through it — one sweep's ink is the trace's own colour,
and where several agree it clamps there rather than going white — so the display brightens where the
signal is steady and dims where it only passed, **with no depth to choose and no memory that grows
with the acquisitions**: the cost is the screen's times the traces on it (2.2 MB over the probe's
four one-signal rows; the default four signals in one 800x480 row are 6 MB) and a thousand
acquisitions cost the same. Each signal is a separate image, so two signals agreeing in a cell read
as two colours and not one blended grey. `fades by` picks how it decays: `acquisitions` (each
acquisition keeps the factor of what was before it, 0.5 by default — the phosphor afterglow
schedule) or `time` (a time constant in stream seconds, so the display fades while you watch
whatever the trigger rate). It applies in **trigger mode only** — while a captured acquisition is
on screen — and it goes away with the trigger, on a pause and on an opened file, because the image
is an image *of* a stream of acquisitions. Like the invalid-span bands it reserves no layout space
and nothing on it can be picked. The `[density-probe]` run that measured it is in
`docs/claude/probe-readings.md`; what it costs is in the same place (`[density-probe] frame cost:`:
tick 0.74 → 9.99 ms and draw 2.09 → 20.91 ms over the same run, at four rows and 2.2 M cells a
frame, on llvmpipe — measured before the per-trace split, when the one image was still re-filled
per row).
with the acquisitions**: the cost is bounded, and the bound is the grid's — at most a mebibyte of
counts a signal, so the app's four signals hold 4 MiB between them however wide the window, and the
probe's four one-signal rows read `cells=1044714` (4 178 856 bytes, 1 044 714 of them uploaded on a
moved frame) — and a thousand acquisitions cost the same. Each signal is a separate image, so two
signals agreeing in a cell read as two colours and not one blended grey. `fades by` picks how it
decays: `acquisitions` (each acquisition keeps the factor of what was before it, 0.5 by default —
the phosphor afterglow schedule) or `time` (a time constant in stream seconds, so the display fades
while you watch whatever the trigger rate). It applies in **trigger mode only** — while a captured
acquisition is on screen — and it goes away with the trigger, on a pause and on an opened file,
because the image is an image *of* a stream of acquisitions. Like the invalid-span bands it reserves
no layout space and nothing on it can be picked. The `[density-probe]` run that measured it is in
`docs/claude/probe-readings.md`; what it costs is there and in the plotter's
`docs/data-and-performance.md`, where the density's own pass is priced headless at a fixed geometry —
the app's in-run A/B of it is a mean against a mean and the "before" side includes the run's own
startup frames. A 3040x1700 framebuffer with four rows of one trace and a 2 s window (the frozen
configuration: at 2x device scale that is a 1520x850 window) read **4.5 M cells a trace, 12.9 ms of
tick and 12.8 ms of draw a frame and 4.9 MB uploaded** on llvmpipe, and reads **1.0 M cells, 3.9 /
5.6 ms and 1.0 MB** at the cap.
**And it says what it costs.** A retained acquisition is a whole window copied out of the ring, so
the retention is bounded by two things: the depth, and **`--buffer-mb`** — persistence may hold at