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.
This commit is contained in:
1 parent
3555ab63d7
commit
8f45108559
11 files changed
+492
-70
No files matched your search
@@ -58,7 +58,9 @@ struct FrameStats {
|
||||
/// persistent-mapped, fence-guarded ring on 4.3+/ARB_buffer_storage, and the
|
||||
/// orphan-and-refill baseline on 3.3. Setting `PLOT_FORCE_GL33_UPLOAD=1` in the
|
||||
/// environment forces the baseline path, which is how both are exercised on one
|
||||
/// machine.
|
||||
/// machine. A driver that refuses the storage, a map or a fence drops the store
|
||||
/// that hit it to the baseline path -- the ring is never rotated through without
|
||||
/// a guard -- and the frame reports it (see `last_frame_had_gl_error()`).
|
||||
///
|
||||
/// Not thread-safe, and confined to the thread that owns the current context.
|
||||
class Renderer {
|
||||
@@ -87,6 +89,10 @@ class Renderer {
|
||||
/// the renderer draws with -- set colours and metrics here, not on the plot.
|
||||
/// The plot also borrows this renderer's font (when it has no backend of its
|
||||
/// own), so the renderer must outlive the plot's use of it.
|
||||
///
|
||||
/// `Plot::layout()` is computed exactly once, here, and handed to every helper:
|
||||
/// it runs the auto scale, the strip fixed-point loop and the grid build, so
|
||||
/// re-deriving it per helper would rebuild the ticks and the grid per helper.
|
||||
void draw(Plot& plot);
|
||||
|
||||
/// Ends the frame, draining `glGetError()` in a loop so a stale error cannot be
|
||||
@@ -107,7 +113,10 @@ class Renderer {
|
||||
render::Style& style() noexcept { return style_; }
|
||||
const render::Style& style() const noexcept { return style_; }
|
||||
|
||||
/// True when the last `end_frame()` found a GL error. Errors are drained in a
|
||||
/// True when the last frame reported a GL failure: an error drained from
|
||||
/// `glGetError()` in `end_frame()`, or a fence the driver refused, which the
|
||||
/// store reports when the draw that should have been guarded is issued (such a
|
||||
/// frame has already fallen back to the baseline path). Errors are drained in a
|
||||
/// loop, so a stale one cannot be attributed to a later frame.
|
||||
bool last_frame_had_gl_error() const noexcept { return gl_error_; }
|
||||
|
||||
@@ -115,9 +124,14 @@ class Renderer {
|
||||
const FrameStats& stats() const noexcept { return stats_; }
|
||||
|
||||
bool ready() const noexcept { return ready_; }
|
||||
/// True when `init()` selected the persistent-mapped transfer (4.3+ with
|
||||
/// ARB_buffer_storage, and `PLOT_FORCE_GL33_UPLOAD` unset).
|
||||
bool using_persistent_upload() const noexcept { return persistent_; }
|
||||
/// True while both pipelines are uploading through the persistent-mapped ring:
|
||||
/// `init()` selects it from the caps and the entry points (4.3+ with
|
||||
/// ARB_buffer_storage, and `PLOT_FORCE_GL33_UPLOAD` unset), and a driver failure
|
||||
/// later -- a refused map or fence -- leaves the store that hit it on the
|
||||
/// baseline path, which this then says.
|
||||
bool using_persistent_upload() const noexcept {
|
||||
return line_vao_.persistent() && text_vao_.persistent();
|
||||
}
|
||||
/// The axis text size the renderer actually draws with: `style().axis_font_px`
|
||||
/// rounded to the nearest whole multiple of `render::BitmapFont::kGlyphPx`,
|
||||
/// because a 1-bit glyph only scales cleanly by whole factors.
|
||||
@@ -191,11 +205,15 @@ class Renderer {
|
||||
|
||||
void draw_grid(const Plot& plot);
|
||||
void draw_axis_lines(const Plot& plot);
|
||||
void draw_tick_labels(const Plot& plot);
|
||||
void draw_titles(const Plot& plot);
|
||||
void draw_traces(const Plot& plot);
|
||||
void draw_cursors(const Plot& plot);
|
||||
void draw_measures(const Plot& plot);
|
||||
/// The chrome helpers take the frame's layout: `plot.layout()` recomputes the
|
||||
/// ticks, the strips and the grid, so it belongs to `draw()` alone.
|
||||
void draw_tick_labels(const Plot& plot, const PlotLayout& layout);
|
||||
void draw_titles(const Plot& plot, const PlotLayout& layout);
|
||||
/// The data helpers take it too; they read the axes rect and the transforms it
|
||||
/// pins.
|
||||
void draw_traces(const Plot& plot, const PlotLayout& layout);
|
||||
void draw_cursors(const Plot& plot, const PlotLayout& layout);
|
||||
void draw_measures(const Plot& plot, const PlotLayout& layout);
|
||||
void draw_annotations(const Plot& plot);
|
||||
|
||||
render::BitmapFont font_;
|
||||
@@ -218,7 +236,6 @@ class Renderer {
|
||||
u32 fb_w_ = 0;
|
||||
u32 fb_h_ = 0;
|
||||
bool ready_ = false;
|
||||
bool persistent_ = false;
|
||||
bool gl_error_ = false;
|
||||
FrameStats stats_{};
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user