feat: a dense trace's columns are graded by dwell, so density reads as brightness
Stage 1 cured the moire by collapsing a dense gathered window onto one vertical segment per pixel column; this is the information that collapse throws away. A column's min and max are the same whether its samples are spread evenly or piled at its two ends, so the flat parts of a square wave and the edges between them drew as one ink. Style::intensity_grading, which the library leaves off and this app turns on at the first tick (the renderer does not exist until the area is realized), grades each column by the time the signal spent at each row -- the digital-phosphor look. frame path: gains a graded= field, which must equal columns=: grading is a way of drawing the reduction, not a third path. UDPSCOPE_NO_PHOSPHOR=1 puts the style back to the library's default and is the A/B, since a frame mean cannot attribute its own cost -- measured on the fast mock, draw 3.24 ms graded against 2.30 ms, with segments 1340/4157 against 131/4306 over the same lod=453 frames. That A/B is also what found the level-floor regression now fixed upstream. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
5f5b87ca60
commit
b4b26d3e31
5 files changed
+112
-4
No files matched your search
@@ -42,6 +42,12 @@ The full table is in the shortcut overview (<tt>Ctrl+/</tt>).
|
||||
pixel grid — which reads as bands of light and dark that <i>crawl</i> as you pan. One line per
|
||||
column removes the beat, and it is also much faster: a 1 MS/s view costs a couple of thousand
|
||||
line segments a frame instead of a couple of hundred thousand.
|
||||
• Those columns are <i>graded by brightness</i>, the way a digital-phosphor scope reads: bright
|
||||
where the signal spent its time, faint where it only passed through. A column's highest and
|
||||
lowest value are the same whether the samples in it are spread evenly or piled at its two ends,
|
||||
so the grading is what tells the two apart — the flat parts of a square wave come out solid and
|
||||
the edges between them faint. It is read from a finer division of each column rather than from
|
||||
the line that is drawn, and it costs about a millisecond a frame.
|
||||
|
||||
<b>Example</b>
|
||||
|
||||
|
||||
+28
-2
@@ -311,6 +311,9 @@ struct App {
|
||||
unsigned signal_generation = ~0u;
|
||||
bool updating_panels = false; ///< guards signal handlers during a rebuild
|
||||
bool relayout_pending = false;
|
||||
/// True once the renderer's `Style` has been stated (see `tick()`): the renderer does not
|
||||
/// exist until the area is realized, so the one-shot has to be a tick's job.
|
||||
bool style_stated = false;
|
||||
bool signals_panel_initialised = false;
|
||||
/// Set once the user edits the trigger level, so the panel stops defaulting it
|
||||
/// into the middle of the declared range.
|
||||
@@ -417,6 +420,11 @@ struct App {
|
||||
/// produced them -- and it is the only reading that distinguishes "the segment count
|
||||
/// collapsed because the reduction ran" from "the window got narrower".
|
||||
int dbg_column_frames = 0;
|
||||
/// Frames on which at least one reduced column was drawn **graded by dwell** rather than
|
||||
/// as one flat band (`Style::intensity_grading`, which this app turns on). A subset of
|
||||
/// `columns`: grading is a way of drawing the reduction, not a third path, so the two
|
||||
/// counts being equal is what says the look is reaching every reduced frame.
|
||||
int dbg_graded_frames = 0;
|
||||
/// Painted frames on which `draw_traces` drew **no trace at all**, and the last one's index.
|
||||
/// This is the observable for "the plot flickers as if there were no data": `staged_segments`
|
||||
/// cannot see it, because the grid and the instruments stage geometry of their own, so a frame
|
||||
@@ -1254,6 +1262,7 @@ int on_render_probe(GtkGLArea*, GdkGLContext*, gpointer user) {
|
||||
if (s.raw_traces > 0) ++app->dbg_raw_frames;
|
||||
if (s.lod_traces > 0) ++app->dbg_lod_frames;
|
||||
if (s.column_traces > 0) ++app->dbg_column_frames;
|
||||
if (s.graded_traces > 0) ++app->dbg_graded_frames;
|
||||
if (s.raw_traces > 0 || s.lod_traces > 0) check_frame_ink(*app);
|
||||
if (s.raw_traces == 0 && s.lod_traces == 0) {
|
||||
++app->dbg_notrace_frames;
|
||||
@@ -4872,6 +4881,22 @@ gboolean tick(gpointer user) {
|
||||
}
|
||||
}
|
||||
|
||||
// The trace look, stated on the first tick that finds a renderer -- there is none until
|
||||
// the area is realized, and this is the same "claim it when it exists" shape as the frame
|
||||
// clock above. `intensity_grading` is off in the library by default (it is a change of
|
||||
// look, not a correction, and other hosts must not be restyled by an upgrade); an
|
||||
// oscilloscope is exactly the host it is for, so udpscope turns it on. A one-shot rather
|
||||
// than a per-frame write because `Style` is plain data and nothing else touches it.
|
||||
if (!app->style_stated && app->area != nullptr) {
|
||||
if (plot::Renderer* r = app->area->renderer()) {
|
||||
// `UDPSCOPE_NO_PHOSPHOR` puts it back to the library's default, which is the only way
|
||||
// to read `frame cost:` and `frame path:` against the ungraded path -- a frame mean
|
||||
// cannot attribute its own cost, so the A/B is the measurement.
|
||||
r->style().intensity_grading = g_getenv("UDPSCOPE_NO_PHOSPHOR") == nullptr;
|
||||
app->style_stated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (app->relayout_pending) {
|
||||
app->relayout_pending = false;
|
||||
relayout_areas(*app);
|
||||
@@ -8219,9 +8244,10 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
|
||||
app.dbg_draw_ms_max, app.dbg_draw_max_frame,
|
||||
app.dbg_paint_n > 0 ? app.dbg_paint_ms_sum / app.dbg_paint_n : 0.0,
|
||||
app.dbg_paint_ms_max, app.dbg_paint_n);
|
||||
std::printf("[udpscope] frame path: raw=%d lod=%d columns=%d frames, segments %llu/%u "
|
||||
"(mean/max), visible max %llu samples\n",
|
||||
std::printf("[udpscope] frame path: raw=%d lod=%d columns=%d graded=%d frames, segments "
|
||||
"%llu/%u (mean/max), visible max %llu samples\n",
|
||||
app.dbg_raw_frames, app.dbg_lod_frames, app.dbg_column_frames,
|
||||
app.dbg_graded_frames,
|
||||
static_cast<unsigned long long>(app.dbg_paint_n > 0
|
||||
? app.dbg_seg_sum / app.dbg_paint_n
|
||||
: 0),
|
||||
|
||||
Reference in new issue
Block a user