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
@@ -27,26 +27,50 @@ class Handle {
|
||||
GLuint id() const noexcept { return id_; }
|
||||
explicit operator bool() const noexcept { return id_ != 0; }
|
||||
|
||||
/// Identity of the object this handle names, as a counter that only ever
|
||||
/// advances: every `reset()` that deleted a name, every allocating `create()`
|
||||
/// and every move advances it. A driver is free to hand out a name it has
|
||||
/// already used, so state that captured the old object -- the attribute
|
||||
/// pointers of a vertex array above all -- must compare this counter rather
|
||||
/// than the name (see `render::detail::InstanceStore::epoch()`).
|
||||
u32 generation() const noexcept { return generation_; }
|
||||
|
||||
/// Deletes the GL object, if any, and empties the handle.
|
||||
void reset() noexcept {
|
||||
if (id_ != 0) {
|
||||
Derived::destroy(id_);
|
||||
id_ = 0;
|
||||
++generation_;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
Handle(Handle&& other) noexcept : id_(other.id_) { other.id_ = 0; }
|
||||
Handle(Handle&& other) noexcept : id_(other.id_), generation_(other.generation_) {
|
||||
other.id_ = 0;
|
||||
++generation_; // the object is this handle's now
|
||||
++other.generation_; // and no longer the other's
|
||||
}
|
||||
Handle& operator=(Handle&& other) noexcept {
|
||||
if (this != &other) {
|
||||
reset();
|
||||
reset(); // advances the generation when it deleted a name
|
||||
id_ = other.id_;
|
||||
other.id_ = 0;
|
||||
++generation_;
|
||||
++other.generation_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Advances the generation for a name the derived class has just allocated and
|
||||
/// stored: every allocating `create()` calls it, so the counter is a complete
|
||||
/// record of the handle's identity. A failed allocation needs no call -- it has
|
||||
/// already gone through `reset()`, which advanced it.
|
||||
void note_allocated() noexcept { ++generation_; }
|
||||
|
||||
GLuint id_ = 0;
|
||||
|
||||
private:
|
||||
u32 generation_ = 0;
|
||||
};
|
||||
|
||||
/// Vertex buffer object. All fallible operations report through `Status`;
|
||||
|
||||
Reference in new issue
Block a user