feat(render): a reduced column can be graded by dwell, not drawn as one flat band
CI / debug (tests + all adapters) (push) Canceled after 0s
CI / coverage (floor + measured table) (push) Canceled after 0s
CI / asan (AddressSanitizer) (push) Canceled after 0s

A pixel column reduced from a dense envelope is honest about where the signal
went and silent about how long it was there: its min and max are identical
whether its samples fill the span or pile at its two ends, so a ramp and a pulse
are one band. Style::intensity_grading grades it instead -- bright where the
signal sits, faint where it only passes through -- by asking the gather for a
level finer than the pixel grid and reading the distribution from those
sub-buckets. Off by default: a change of look, not a correction of one.

The measure is dwell per row, each segment depositing one unit spread over the
rows it spans; counting +1 per row instead grades a sine's fast middle as
brightly as its rails, which is the pattern inverted. The accumulator is one
column wide (a full w*h density image is 33 MB at 4K) with each span written as
two delta writes, and normalisation is against the column's own peak -- a
frame-wide scale fades the trace out exactly where it moves fastest. Alpha is
quantised into tiers, one LineBatch and one draw call each, because the line
pipeline's colour is a uniform and a per-segment alpha is inexpressible.

The finer ask is floored at level 1 whenever the flat path would have used the
pyramid: level_for() answers 0 -- raw samples -- as soon as a sub-column falls
under one bucket, so an unfloored ask turns a moderately dense frame into an
O(visible) gather (measured at 172 samples a pixel: raw=1154 lod=0 and 6.4 ms a
frame against raw=710 lod=453 and 2.3 ms). Grading is a way of drawing a
reduction and may not undo it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Opus 4.6 committed 2026-10-06 03:02:36 +02:00
1 parent e81e84645f
commit cc908efa36
6 files changed
+629 -13

No files matched your search

+60
View File
@@ -216,6 +216,66 @@ polyline, over one sine period across 60 000 samples. A count of *partially cove
always spills into its neighbour column, so the two renderings measure 577 and 652, which
separates nothing. The frame's whole geometry collapses with it: 627 segments against 60 054.
## A reduced column can be graded by dwell
`Style::intensity_grading` draws each of those columns **graded** rather than as one flat band:
bright where the signal sits, faint where it only passes through, the way a digital-phosphor
scope reads. It is off by default — a change of look, not a correction of one — and
`FrameStats::graded_traces` counts the traces that took it, a subset of `column_traces`, since
grading is a way of drawing the reduction and not a third path.
What it puts back is the information the reduction throws away: a column's min and max are the
**same** whether its samples fill the span evenly or pile at its two ends, so a ramp and a pulse
are one band. The distribution cannot be recovered from the band, which is why the gather is
asked at a level **finer than the pixel grid** (`intensity_sub_columns` sub-buckets per column)
and the dwell read from those sub-buckets.
Five things carry it, and each was arrived at by being wrong first:
- **The measure is dwell per row, each segment depositing one unit spread over the rows it
spans** (`1 / height`). Counting +1 per row instead grades a sine's fast middle as brightly as
its rails, which is the pattern inverted — the middle is where the signal spends the *least*
time.
- **The accumulator is one column wide.** Gathered x is monotonic, so points arrive column by
column: accumulate, read, clear. A full `axes.w × axes.h` density image is 33 MB at 4K; one
column of `axes.h` floats is L1-resident. A span is written as a `+1/height` delta at its first
row and the negation one past its last, so it costs two writes whatever its height, and the
running sum over the column is the per-row dwell.
- **Normalisation is per column, against the column's own peak.** A frame-wide scale needs the
whole image to exist before anything can be drawn, *and* reads worse: a column the signal flew
through uniformly comes out faint at every row, so the trace fades out exactly where it moves
fastest. The cost of the local scale is one extra non-clearing walk of the accumulator to find
the peak.
- **Alpha is quantised into `intensity_steps` tiers, one `LineBatch` and one draw call each.**
The line pipeline's colour is a **uniform**, not per-instance (`LineInstance` is four floats of
geometry; only `TextInstance` carries `a_colour`), so a per-segment alpha is inexpressible and
the tiers are what make the gradient drawable at all. `intensity_floor` keeps the faintest tier
visible — a fast riser is one sub-bucket deep everywhere and must not be dimmed out of
existence — and `intensity_gamma` lifts the faint end. A connector between two disjoint columns
is emitted at the **faintest** tier: it is inferred geometry, not a place the signal was
measured to dwell.
- **Grading may not undo the reduction.** `level_for()` answers 0 — raw samples — as soon as the
asked-for fraction of a column falls under one bucket (`kBucket` is 64), so the finer ask is
**floored at level 1** whenever the flat path would have used the pyramid. Without the floor
every moderately dense frame became an O(visible) gather: measured in udpscope at 1.3 MS/s over
a 2 s window, 172 samples a pixel, the flat path sat on the pyramid and grading dropped the
whole figure to raw (`raw=1154 lod=0` against `raw=710 lod=453`) and cost 2.3 → 6.4 ms of draw
mean. The floor degrades the *look* instead — fewer sub-columns than asked for, down to what the
coarsest buckets give, which at 64 samples a bucket is still two or three per column. That is
also why raising `intensity_sub_columns` past about 8 buys nothing: the bucket size is the
limit, not the number.
`a_dense_trace_can_be_graded_by_dwell` is the guard, and it draws the same frame both ways. It
profiles the mean peak coverage over the band's two outer fifths against its middle fifth: the
flat band is flat (**191 and 191**, one ink) and the graded one is **191 against 131**, a middle
at 0.69 of the edge. That ratio is a *tier* ratio and not the dwell ratio — the default gamma
0.45 and floor 0.3 compress it on purpose. The geometry stays bounded by the axis and not by its
area: **8303 segments over a 572 px axis**, 14.5 runs a column, against the flat path's 627 at
one end and `572 × 156` rows at the other. A fourth frame over a window wide enough that the
flat path is on the pyramid is what exercises the floor (`raw=0 lod=1 … seg=634
visible=200001`); the test's first window sits at 105 samples a pixel, where both paths are raw
and the floor is never reached.
## One sample or a statistic of many
Every number the library reports is one the samples actually hold. What differs between