refactor(render): TextBackend owns its atlas, and staged glyphs carry a rotation basis

This commit is contained in:
Martino Ferrari committed 2026-09-28 09:57:28 +02:00
1 parent 90e17a0a11
commit e074f57ddf
23 files changed
+1073 -227

No files matched your search

+23 -3
View File
@@ -16,17 +16,37 @@ struct LineInstance {
f32 y1;
};
/// Per-instance payload of the text pipeline: the glyph quad's top-left corner
/// in Rect pixels plus the glyph's atlas texel origin.
/// Per-instance payload of the text pipeline: one glyph cell.
///
/// - `(x, y)` is the cell's top-left corner in Rect pixels, top-left origin like
/// `plot::Rect`. `TextBatch` already applied the glyph's bearing along the
/// basis, so this is the corner the shader grows the cell from.
/// - `(u, v)` is the glyph rect's **normalised top-left** in the atlas, with `v`
/// increasing *downward* -- the convention `Glyph` states, so `(u, v)` is the
/// rect's top-left texel and the cell spans `(u, v)..(u + w, v + h)`, where
/// `(w, h)` is the glyph cell's normalised size (a per-draw uniform: every
/// glyph a batch stages comes from one atlas and occupies a cell of one size).
/// - `(ax, ay)` is the glyph's local +x axis -- the direction the run advances
/// in -- as a **unit vector in pixels**. It is what rotates the cell: the
/// shader builds it from `(ax, ay)` and its perpendicular, and maps the glyph's
/// rect onto the two the same way round, so a cell is never mirrored. `(1, 0)`
/// is unrotated text, and `(0, -1)` is a run advancing up the screen, i.e. a
/// 90 degree counter-clockwise label such as a y-axis title.
///
/// Six floats, so the whole text pipeline still fits one instanced draw call per
/// frame with rotation free: a matrix per glyph would be three times the bytes
/// for the same pixels.
struct TextInstance {
f32 x;
f32 y;
f32 u;
f32 v;
f32 ax;
f32 ay;
};
static_assert(sizeof(LineInstance) == 4 * sizeof(f32), "line instance must stay 4 floats");
static_assert(sizeof(TextInstance) == 4 * sizeof(f32), "text instance must stay 4 floats");
static_assert(sizeof(TextInstance) == 6 * sizeof(f32), "text instance must stay 6 floats");
/// How a store's staged instances reach the GPU. Semantics are identical either
/// way -- only the transfer differs, and `Renderer::init()` picks one from