perf: a frozen frame is not re-rendered sixty times a second
The tick queued a GL render unconditionally. In `Held` and `Paused` the rows are bound to an immutable snapshot on a pinned axis, so a frame in which none of the picture's own terms moved is the same frame again. The gate keys on a coarse signature of what a frozen view can be re-framed by, opens for `kSettleRenders` frames on a change because the figure lays out per render, and has a floor that renders anyway every few ticks. The layout editor is excluded: its buttons are placed from the last frame's rects, so a skipped frame there is a stale widget rather than a stale picture. `skips=` in the probe dump is the reading. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
84c158ff21
commit
be71a1d4bb
5 files changed
+130
-4
No files matched your search
@@ -503,6 +503,23 @@ struct App {
|
||||
/// 0 until the first event of the session.
|
||||
gint64 last_latch_us = 0;
|
||||
double timed_edge = 0.0;
|
||||
/// The render gate's memory (see the end of `tick()`). A `Held` or `Paused` view is bound to
|
||||
/// an immutable snapshot on a pinned axis, so a frame whose picture cannot have changed does
|
||||
/// not need to be drawn again -- and on llvmpipe, or on a battery, re-drawing a frozen event
|
||||
/// sixty times a second is the whole cost of the application. The first four are the coarse
|
||||
/// signature of what a frozen picture can be re-framed by; a change to any of them opens
|
||||
/// `render_open` for `kSettleRenders` frames, so the figure's layout floor has the passes it
|
||||
/// needs to converge. `render_skipped` is the floor that makes everything the signature does
|
||||
/// *not* name render soon anyway (within `kStaticRenderEvery` ticks), which is what keeps the
|
||||
/// gate from being able to stall the picture.
|
||||
std::uint64_t render_event_seq = 0;
|
||||
double render_x_min = 0.0;
|
||||
double render_x_max = 0.0;
|
||||
int render_rows = -1;
|
||||
int render_open = 0;
|
||||
int render_skipped = 0;
|
||||
/// Self-check: ticks that queued no render, so the gate is a number rather than a claim.
|
||||
int dbg_render_skips = 0;
|
||||
double window_limit_s = -1.0; ///< the cap the window control was last clamped against
|
||||
/// The rate each channel actually *stored* over the last second, and the declared rate
|
||||
/// beside it. The two differ for two reasons now and both are worth seeing: the declared
|
||||
@@ -1021,6 +1038,18 @@ struct RowMenuProbeState {
|
||||
};
|
||||
|
||||
constexpr int kTickMs = 16;
|
||||
/// How often a frozen view is re-drawn anyway, in ticks: the render gate's floor (see `tick()`
|
||||
/// and `App::render_skipped`). Four is ~15 Hz, so anything the gate's signature does not name --
|
||||
/// a recolour, a measurement's mark, a y range a fit changed -- reaches the screen within ~64 ms
|
||||
/// of a still picture, which is below noticing. It is deliberately a small number: the gate's
|
||||
/// purpose is to stop spending a GPU on an unchanging frame, not to shave the last frame off.
|
||||
constexpr int kStaticRenderEvery = 4;
|
||||
/// How many frames one change to the gate's signature is worth (see `tick()` and
|
||||
/// `App::render_open`). More than one because the figure lays itself out *per render* and its left
|
||||
/// gutter is a monotone floor that takes two passes to converge (the `layout over N frames`
|
||||
/// reading): a re-framed frozen view drawn once would sit a pass short of its final gutter until
|
||||
/// the floor above fired. Three leaves a pass in hand without being a frame budget.
|
||||
constexpr int kSettleRenders = 3;
|
||||
/// Below this window width the sidebar has no room beside the plot, and the split view overlays it
|
||||
/// instead of squeezing it. See `tick()`.
|
||||
constexpr int kSidebarRoom = 900;
|
||||
|
||||
Reference in new issue
Block a user