Commit Graph
6 Commits
Author SHA1 Message Date
Martino Ferrari e074f57ddf refactor(render): TextBackend owns its atlas, and staged glyphs carry a rotation basis 2026-09-28 09:57:28 +02:00
Martino Ferrari a08b184481 fix(plot,render,gl): live streaming measurements, handed-back GL state
Final whole-branch review fixes.

1 (Important). Plot::measurement() cached on (series pointer, sample count)
only, so a saturated StreamSeries -- whose size() stays pinned while x0()
advances every acquire() -- froze its readout on the first saturated frame
until a cursor moved. The cache key now carries the series' x0() (new
MeasureSlot::last_x0), so a sliding window recomputes and a static series
still does not: asserted with a new macro-gated counter hook
(plot/detail/measure_count.hpp, PLOT_COUNT_MEASURE_RECOMPUTES, wired like the
pyramid and ring counters, absent from the release library). New case
saturated_stream_measurement_tracks_the_sliding_window: saturated ring,
stationary cursors, ramp in absolute x -- zero recomputes for repeated reads
with nothing moving, one per acquire while it slides.

2-3, 9. Spec: 4.3 described a GL_LINE_STRIP path that does not exist (there is
one rasteriser -- instanced anti-aliased quads -- and dense_threshold_spp
selects the gather level, not a primitive); 8 named a PLOT_GL_CHECK() macro
that exists nowhere, and stated no exception policy. The table, the field's
comment and 8 now describe the real gather-level threshold, the real
glGetError() drain plus the stores' drop-to-orphan fallbacks, and the real
policy: library failures return Status/bool, allocating entry points propagate
std::bad_alloc.

4. The frame handed back neither GL_UNPACK_ALIGNMENT (init() sets 1 for the
atlas) nor the clear colour (queried once with a new glGetFloatv entry).
end_frame() restores both and renderer.hpp lists them in the residue
contract.

5. draw() kept a host TextBackend for layout while drawing with its own font.
The renderer now installs its font unconditionally -- strips and labels measure
and draw with one backend -- with a debug-build diagnostic if a different one
was set, and Plot::set_text_backend() is documented as the no-renderer path.
New case renderer_measures_the_labels_it_draws asserts layout, measuring and
the drawn label's ink agree.

6-10. draw()'s style overwrite stated at the call site; the load-sensitive
wall-clock bound in test_measure.cpp replaced by a counter assertion (same
interval, same fold count over a 512x length ratio); dump_frame_ppm drops
noexcept and says why it may allocate; error.txt ignored (not this project's,
not generated here) rather than deleted.

Verified: debug and release build clean (-Wall -Wextra -Wpedantic -Wconversion
-Wshadow), 4/4 ctest entries pass, 130 cases pass (was 127), umbrella and
header self-containment checks pass, and no counter symbol in the release
archive.
2026-09-28 03:42:34 +02:00
Martino Ferrari 8f45108559 fix(render): bind identity into the epoch, titles into reserved strips, one layout per frame
Four review findings on the Task 7 renderer:

- InstanceStore::epoch() is derived from gl::Buffer::generation() -- advanced by
  every allocation, deletion and move -- plus the slot the instances were written
  to, so the orphan fallback of grow_persistent() can no longer leave the vertex
  array's attribute pointers bound to a deleted buffer: the epoch reports the new
  identity and upload() re-points them before the draw.
- PlotLayout reserves y_title_area/x_title_area outside axes (sized from the text
  metrics) and draw_titles() draws in them, so a title can no longer land on the
  data, whatever the envelope does.
- draw() computes plot.layout() once and hands it to the helpers, instead of six
  times per frame, each of which rebuilt the ticks and the grid.
- A refused fence is no longer dropped: mark_drawn() falls back to the orphan path
  and reports the failure rather than rotating through an unsynchronised slot.

New smoke cases force both refusals through the GL entry points (glMapBufferRange,
glFenceSync) and assert the pixels plus the live transfer mode; the title case
asserts the title colour is absent from the bands inside axes and present in both
strips. All three fail against the pre-fix sources.
2026-09-27 23:19:31 +02:00
Martino Ferrari 3555ab63d7 feat(render): Renderer with LOD selection, 3.3 and 4.3+ upload paths, headless smoke test 2026-09-27 23:03:08 +02:00
Martino Ferrari 7cbc1c9955 feat(render): style, GLSL line/text pipelines, staging batches, bitmap font 2026-09-27 22:03:56 +02:00
Martino Ferrari 258caa38a7 feat(gl): dependency-free GL loader, RAII handles, caps probe, headless EGL harness 2026-09-27 21:07:36 +02:00