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:
1 parent
da826f2cb5
commit
ac054a30c9
11 files changed
+131
-76
No files matched your search
@@ -102,8 +102,8 @@ struct LayoutState {
|
||||
/// for: it is what the display does with the acquisitions, and there is no second switch.
|
||||
///
|
||||
/// This is the **multiple event** display -- N retained acquisitions, drawn as N traces. The
|
||||
/// phosphor density beside it is a different display with a different cost (one screen-sized
|
||||
/// image against N window copies), which is why it has its own four fields below and why this
|
||||
/// phosphor density beside it is a different display with a different cost (one bounded image a
|
||||
/// trace against N window copies), which is why it has its own four fields below and why this
|
||||
/// one keeps the key it has always had: an older file's `trigger_persistence` is still the
|
||||
/// multiple-event depth.
|
||||
int persistence = 1;
|
||||
|
||||
+2
-1
@@ -55,7 +55,8 @@ struct TriggerConfig {
|
||||
|
||||
/// The **phosphor density** (the plotter's `Plot::set_density()`): off by default, and trigger
|
||||
/// mode only. Where `persist_depth` keeps N whole acquisitions as N traces, this accumulates
|
||||
/// every acquisition into one screen-sized intensity image -- brightness is how often a trace
|
||||
/// every acquisition into one intensity image **per trace** (the axes rect in device pixels,
|
||||
/// bounded by the plotter's `kDensityCellCap`) -- brightness is how often a trace
|
||||
/// passed a cell, so the cost of a display of a thousand acquisitions is the cost of one, and
|
||||
/// there is no depth to get wrong. The image is drawn behind the traces, it is unpickable and it
|
||||
/// reserves no layout space, exactly as the invalid-span bands do.
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
|
||||
Both displays live here and they are two different things: *multiple events* keeps the
|
||||
last N acquisitions as N traces, dimmer with age, while *persistence* accumulates every
|
||||
acquisition into an image (one screen-sized grid per trace, in that trace's own colour,
|
||||
acquisition into an image (one grid per trace, in that trace's own colour,
|
||||
the plotter's `Plot::set_density()`), drawn in place of the traces. The first is a
|
||||
count and the second is a decay, which is why neither is a mode of the other. -->
|
||||
<object class="GtkExpander" id="advanced_exp">
|
||||
|
||||
+39
-19
@@ -40,26 +40,46 @@ int on_render_probe(GtkGLArea*, GdkGLContext*, gpointer user) {
|
||||
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;
|
||||
app->dbg_notrace_last = app->dbg_paint_n;
|
||||
if (app->dbg_ever_bound) {
|
||||
++app->dbg_notrace_after_bound;
|
||||
// What the frame was asked to draw, beside what there was to draw it from: an axis
|
||||
// that left the data and a trace that had nothing bound are the same picture and
|
||||
// different bugs, so both are named here rather than inferred from the count.
|
||||
if (app->dbg_notrace_after_bound <= 40) {
|
||||
const RowSet::Row& r = app->rows->row(0);
|
||||
const plot::Range ax = r.plot != nullptr ? r.plot->x_range(r.axis_x) : plot::Range{};
|
||||
std::fprintf(stderr, "[notrace] paint %d mode=%s axis=[%.6f..%.6f] segments=%u",
|
||||
app->dbg_paint_n, display_mode_name(app->dbg_display), ax.min, ax.max,
|
||||
s.staged_segments);
|
||||
for (int sl = 0; sl < RowSet::kSlotsPerRow; ++sl) {
|
||||
if (r.channel[static_cast<std::size_t>(sl)] < 0) continue;
|
||||
const plot::Range b = r.slots[static_cast<std::size_t>(sl)].x_bounds();
|
||||
std::fprintf(stderr, " slot%d n=%zu x=[%.6f..%.6f]", sl,
|
||||
r.slots[static_cast<std::size_t>(sl)].size(), b.min, b.max);
|
||||
// **A frame the density is drawing is not a traceless one.** `Renderer::draw_traces()`
|
||||
// returns while a plot's phosphor density is on -- the trace ink *is* the image -- so every
|
||||
// frame of a persistence display stages no trace and would be counted here, which inflates
|
||||
// the dump's reading and (with `UDPSCOPE_PROBE_NOTRACE` set below) prints a line per frame.
|
||||
// That is the display working, not "what the frame was asked to draw" with nothing to draw
|
||||
// it from. Read off the plots, because the frame's own stats can only say no trace was
|
||||
// drawn and not why.
|
||||
bool density_on = false;
|
||||
for (int i = 0; i < app->rows->row_count() && !density_on; ++i) {
|
||||
const plot::Plot* p = app->rows->row(i).plot;
|
||||
density_on = p != nullptr && p->density().enabled;
|
||||
}
|
||||
if (!density_on) {
|
||||
++app->dbg_notrace_frames;
|
||||
app->dbg_notrace_last = app->dbg_paint_n;
|
||||
if (app->dbg_ever_bound) {
|
||||
++app->dbg_notrace_after_bound;
|
||||
// What the frame was asked to draw, beside what there was to draw it from: an axis
|
||||
// that left the data and a trace that had nothing bound are the same picture and
|
||||
// different bugs, so both are named here rather than inferred from the count.
|
||||
//
|
||||
// **A per-paint line, so it is behind a variable** (`UDPSCOPE_PROBE_NOTRACE`), unlike
|
||||
// the counters beside it: a state that draws nothing lasts as long as it lasts -- a
|
||||
// `Waiting` tail, a bound row with no data -- and the line would be one per frame the
|
||||
// whole time. The count in the dump is the reading; this is the detail behind it.
|
||||
static const bool notrace_print = g_getenv("UDPSCOPE_PROBE_NOTRACE") != nullptr;
|
||||
if (notrace_print && app->dbg_notrace_after_bound <= 40) {
|
||||
const RowSet::Row& r = app->rows->row(0);
|
||||
const plot::Range ax = r.plot != nullptr ? r.plot->x_range(r.axis_x) : plot::Range{};
|
||||
std::fprintf(stderr, "[notrace] paint %d mode=%s axis=[%.6f..%.6f] segments=%u",
|
||||
app->dbg_paint_n, display_mode_name(app->dbg_display), ax.min, ax.max,
|
||||
s.staged_segments);
|
||||
for (int sl = 0; sl < RowSet::kSlotsPerRow; ++sl) {
|
||||
if (r.channel[static_cast<std::size_t>(sl)] < 0) continue;
|
||||
const plot::Range b = r.slots[static_cast<std::size_t>(sl)].x_bounds();
|
||||
std::fprintf(stderr, " slot%d n=%zu x=[%.6f..%.6f]", sl,
|
||||
r.slots[static_cast<std::size_t>(sl)].size(), b.min, b.max);
|
||||
}
|
||||
std::fprintf(stderr, "\n");
|
||||
}
|
||||
std::fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -756,7 +756,7 @@ GtkWidget* build_trigger_panel(App& app) {
|
||||
"acquisitions are also bounded by the buffer's own size");
|
||||
app.trigger_persist_row = row_for(ui, "persistence_row", app.trigger_persist_spin);
|
||||
|
||||
// The phosphor *density*: one screen-sized image per trace, in that trace's own colour, every
|
||||
// The phosphor *density*: one bounded image per trace, in that trace's own colour, every
|
||||
// acquisition accumulated into it, drawn *in place of* the traces and fading by a time constant
|
||||
// or by a per-acquisition factor -- the other display of the same acquisitions, and nothing like
|
||||
// the multiple-event one above (an image per signal against N retained traces, and no count to
|
||||
@@ -772,7 +772,7 @@ GtkWidget* build_trigger_panel(App& app) {
|
||||
}
|
||||
gtk_widget_set_tooltip_text(
|
||||
app.trigger_density_switch,
|
||||
"accumulate every triggered acquisition into a screen-sized image per signal, in that "
|
||||
"accumulate every triggered acquisition into an image per signal, in that "
|
||||
"signal's own colour, drawn in place of the traces: brightness is how often a trace passed, "
|
||||
"so an image of a thousand acquisitions costs what one costs. Trigger mode only, and cleared "
|
||||
"whenever the display leaves it");
|
||||
|
||||
Reference in new issue
Block a user