fix: a density grid is bounded in cells, not by the window's pixels
A grid was the axes rect in device pixels, so dragging a window wide made the image wider with it -- and the per-frame pass over it is O(cells): the fade multiplies every cell, a host's ink()/peak() read every cell, and a frame the fade moved converts and uploads every cell as one byte. At 2x device scale a 1520x850 window's axes rect is 2877 device pixels wide, so four rows of one trace are 4.5 M cells and 4.9 MB uploaded a frame; measured headless on llvmpipe with a 2 s window, 12.9 ms of tick and 12.8 ms of draw a frame, every frame under a time fade -- a cost that follows the window and has no ceiling. The grid is now the axes rect up to kDensityCellCap (2^18 cells, a mebibyte of counts), scaled down uniformly past it so cells stay square and the picture keeps the axes' aspect, and magnified back over the rect with a linear filter: a cell is a count, so a fragment between two of them is between their two ink levels, which a point sample cannot give once a cell is several pixels wide. The same four rows at the cap are 1.05 M cells, 3.9 ms of tick and 5.6 ms of draw and 1.0 MB uploaded, while a grid under the cap is untouched -- a 640x360 viewport's 573x313 axes is still 179 349 cells, one a device pixel. The image's footprint is the axes rect rounded up to whole device pixels on both sides of the pipe, so a grid under the cap keeps the exact one-texel-a-pixel mapping it has always had (sx/sy come out exactly 1) and only a capped grid reads its cell index through a scale. Moving the decay itself to the GPU was considered and left out: the CPU grid is what ink()/peak() read, what the deposit adds to and what the per-sweep dedupe keys on, so a GPU image would have to be reconciled with it per frame -- a larger change than this bound, and not one to half-do. Tests: a_density_grid_is_capped_whatever_the_window reads the bound off a 3840x2160 viewport (261 504 cells of a 3784x2138 axes, aspect kept), a_capped_density_is_magnified_over_the_axes_rect reads the screen side of it (569x460 cells covering all 832 of the rect's columns), and the density cases now assert their capped figures. 399 cases, 0 failed.
This commit is contained in:
1 parent
1b8b55d3ee
commit
f79a97be9b
12 files changed
+414
-108
No files matched your search
@@ -59,15 +59,19 @@ run touched a GPU, so read it as an upper bound and a record of the software pat
|
||||
(`FrameStats::slope_traces`), so the look does not change as a window narrows past the
|
||||
threshold;
|
||||
- a **phosphor density** a host can turn on for a plot (`Plot::set_density()`, an image rather
|
||||
than a set of retained traces): **one grid per trace**, in that trace's own colour, one `f32`
|
||||
cell per device pixel of the axes rect, drawn *in place of* the traces -- a host may not also
|
||||
draw them over it -- into which each acquisition is deposited, 1.0 per cell a trace crosses, so
|
||||
brightness is how often it passed. It fades either by a **time constant** in stream seconds or by
|
||||
a **per-acquisition factor**, and the newest acquisition is deposited like every other, so it
|
||||
fades too. Memory is the screen's times the traces on screen (a 640x360 viewport's axes is
|
||||
179 349 cells per trace, 717 kB), the deposit walks the axes' columns rather than
|
||||
the samples, and a frame whose grids did not move costs one draw call per trace
|
||||
(`FrameStats::density_cells`/`density_upload_bytes`); see `docs/data-and-performance.md`;
|
||||
than a set of retained traces): **one grid per trace**, in that trace's own colour, the axes
|
||||
rect in device pixels, drawn *in place of* the traces -- a host may not also draw them over it --
|
||||
into which each acquisition is deposited, 1.0 per cell a trace crosses, so brightness is how
|
||||
often it passed. It fades either by a **time constant** in stream seconds or by a
|
||||
**per-acquisition factor**, and the newest acquisition is deposited like every other, so it
|
||||
fades too. The resolution is bounded (`kDensityCellCap`, 262 144 cells -- a mebibyte of counts --
|
||||
a grid, past which the rect is scaled down uniformly and the image magnified back over it with a
|
||||
linear filter), because the per-frame pass is `O(cells)` and the window is not bounded at all:
|
||||
memory is the cap times the traces on screen times 4 bytes, whatever the window (a 640x360
|
||||
viewport's axes is 179 349 cells per trace, 717 kB, under it, while four rows of one trace in a
|
||||
3040x1700 one used to be 4.5 M cells and 18 MB between them). The deposit walks the axes'
|
||||
columns rather than the samples, and a frame whose grids did not move costs one draw call per
|
||||
trace (`FrameStats::density_cells`/`density_upload_bytes`); see `docs/data-and-performance.md`;
|
||||
- sample **markers** when a window is sparse enough that its samples are the shape
|
||||
(`Style::marker_below_samples`, 50 by default, `marker_px` across, in the trace's own colour);
|
||||
- grid with independent major/minor line classes (`Style::grid_major_visible`,
|
||||
|
||||
Reference in new issue
Block a user