refactor(render): the atlas is a declared fixed-cell grid, and staging checks it
The text pipeline stages a glyph's rect *origin* per instance and carries the rect's *size* once per draw, so a backend whose glyphs have differing rect sizes -- a TrueType packing trimmed to ink, which is Task 8's shape -- would have drawn every glyph at whichever size it happened to be told, with nothing downstream able to tell. Make that constraint structural rather than documented: - TextBackend::cell_uv() declares the one cell size, and the contract (an atlas is a fixed-cell grid; a proportional face draws its glyphs *into* a uniform cell instead of trimming each rect to its ink, and why the pipeline needs it) is stated on the atlas accessors. - glyph_is_cell() is the check; add_text()/set_cell() declare the cell from the backend, and the staging path asserts it for every glyph in a debug build. A release build draws on with the cell it was told, as before. - Renderer::flush_text() takes u_glyph_uv from the backend, not from the batch, so the draw's one rect size can never come from whichever glyph was staged first.
This commit is contained in:
1 parent
e074f57ddf
commit
ffc060d6ca
11 files changed
+364
-43
No files matched your search
+11
-5
@@ -136,6 +136,8 @@ const render::Glyph* Renderer::AxisFont::glyph(u32 codepoint) const {
|
||||
return &scaled_;
|
||||
}
|
||||
|
||||
Vec2 Renderer::AxisFont::cell_uv() const { return font_ != nullptr ? font_->cell_uv() : Vec2{}; }
|
||||
|
||||
const u8* Renderer::AxisFont::atlas_pixels() const {
|
||||
return font_ != nullptr ? font_->atlas_pixels() : nullptr;
|
||||
}
|
||||
@@ -262,6 +264,10 @@ void Renderer::draw(Plot& plot) {
|
||||
// measure with the size the text pipeline draws with, and re-read the style
|
||||
// each frame so a changed axis_font_px takes effect on the next one.
|
||||
font_backend_.reset(font_, style_.axis_font_px);
|
||||
// The cell every glyph staged this frame is drawn in, declared from the backend
|
||||
// rather than left to whichever glyph is staged first: the instances carry rect
|
||||
// origins, and the draw carries this size (see TextBackend::cell_uv).
|
||||
glyphs_.set_cell(font_backend_.cell_uv());
|
||||
// A backend whose revision moved -- a font swapped, or re-baked at another size
|
||||
// -- is re-uploaded here, before anything is staged from it, so the frame draws
|
||||
// its glyphs from the atlas they were baked into. The first sync happened in
|
||||
@@ -385,11 +391,11 @@ void Renderer::flush_text() noexcept {
|
||||
upload_text_vertices(glyphs_.data());
|
||||
text_program_.bind();
|
||||
text_program_.set_viewport({viewport_.w, viewport_.h});
|
||||
// The cell comes from the glyphs staged and the atlas they came from -- never
|
||||
// from a constant -- so a font swap moves the quads with the artwork:
|
||||
// `glyph_uv` is the rect every instance is read through and `px_range` is that
|
||||
// same cell on screen.
|
||||
const Vec2 cell_uv = glyphs_.glyph_cell_uv();
|
||||
// The cell comes from the backend and the atlas it uploaded -- never from a
|
||||
// constant, and never from whichever glyph happened to be staged first -- so a
|
||||
// font swap moves the quads with the artwork: `glyph_uv` is the rect every
|
||||
// instance is read through and `px_range` is that same cell on screen.
|
||||
const Vec2 cell_uv = font_backend_.cell_uv();
|
||||
const Vec2 atlas_texels = atlas_.texel_size();
|
||||
text_program_.set_glyph_uv(cell_uv);
|
||||
text_program_.set_px_range({cell_uv.x * atlas_texels.x * font_backend_.scale(),
|
||||
|
||||
Reference in new issue
Block a user