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

+20 -12
View File
@@ -10,6 +10,7 @@
#include <plot/render/primitives.hpp>
#include <plot/render/program.hpp>
#include <plot/render/shaders.hpp>
#include <plot/render/text_atlas.hpp>
#include <plot/render/vertex_array.hpp>
#include <cstdint>
@@ -249,6 +250,7 @@ PLOT_TEST(render_programs_are_inert_until_created) {
text.bind();
text.set_viewport({256.0f, 256.0f});
text.set_px_range({8.0f, 8.0f});
text.set_glyph_uv({1.0f / 16.0f, 1.0f / 7.0f});
text.set_texture_size({128.0f, 56.0f});
text.set_color(plot::Color::hex(0xFFFFFF));
PLOT_CHECK(plot::gl::glGetError() == plot::gl::GL_NO_ERROR);
@@ -338,9 +340,9 @@ PLOT_TEST(line_pipeline_draws_an_aa_strip) {
}
// The text pipeline end to end: the R8 atlas upload, the per-instance glyph UV
// origin, the NEAREST texel-centre sampling, the tint and the same single y
// flip. Every one of the cell's 64 pixels is checked against the glyph bitmap,
// so a wrong atlas cell, a wrong UV or a transposed axis cannot pass.
// rect, the NEAREST texel-centre sampling, the tint and the same single y flip.
// Every one of the cell's 64 pixels is checked against the glyph bitmap, so a
// wrong atlas cell, a wrong UV or a transposed axis cannot pass.
PLOT_TEST(text_pipeline_draws_a_glyph) {
using plot::render::BitmapFont;
plottest::HeadlessContext ctx(256, 256);
@@ -348,22 +350,28 @@ PLOT_TEST(text_pipeline_draws_a_glyph) {
PLOT_CHECK(ctx.create(&st));
PLOT_CHECK(plot::gl::load(nullptr));
// The renderer's atlas: single channel, GL_NEAREST, straight alpha.
plot::gl::Texture atlas;
PLOT_CHECK(atlas.create_r8(BitmapFont::kAtlasWidth, BitmapFont::kAtlasHeight, &st));
atlas.upload_r8(BitmapFont::kAtlasWidth, BitmapFont::kAtlasHeight, BitmapFont::atlas_image());
atlas.set_filter(plot::gl::GL_NEAREST, plot::gl::GL_NEAREST);
// The backend's atlas, through the owner the renderer uses: single channel,
// GL_NEAREST, straight alpha, uploaded from the backend's own bytes.
BitmapFont font;
plot::render::TextAtlas atlas;
PLOT_CHECK(atlas.sync(font, &st));
PLOT_CHECK(!atlas.empty());
PLOT_CHECK_NEAR(atlas.texel_size().x, static_cast<plot::f32>(BitmapFont::kAtlasWidth), 1e-6);
PLOT_CHECK_NEAR(atlas.texel_size().y, static_cast<plot::f32>(BitmapFont::kAtlasHeight), 1e-6);
plot::render::TextProgram program;
PLOT_CHECK(program.create(&st));
plot::render::TextVertexArray glyphs;
PLOT_CHECK(glyphs.create(&st));
BitmapFont font;
plot::render::TextBatch batch;
batch.begin();
batch.add_text({64.0f, 100.0f}, "A", font); // one cell at Rect (64, 100)
// One cell at Rect (64, 100), at the backend's own size: `px` of 1 means one
// font pixel per pixel, which is what makes the texel-to-pixel mapping below
// an identity.
batch.add_text("A", {64.0f, 100.0f}, 1.0f, font);
PLOT_CHECK(batch.glyph_count() == 1);
PLOT_CHECK(batch.data().size() == 6);
glyphs.upload(batch.data().data(), batch.glyph_count());
plot::gl::glViewport(0, 0, static_cast<plot::gl::GLsizei>(ctx.width),
@@ -376,8 +384,8 @@ PLOT_TEST(text_pipeline_draws_a_glyph) {
program.bind();
program.set_viewport({static_cast<f32>(ctx.width), static_cast<f32>(ctx.height)});
program.set_px_range({static_cast<f32>(BitmapFont::kGlyphPx), static_cast<f32>(BitmapFont::kGlyphPx)});
program.set_texture_size({static_cast<f32>(BitmapFont::kAtlasWidth),
static_cast<f32>(BitmapFont::kAtlasHeight)});
program.set_glyph_uv(batch.glyph_cell_uv());
program.set_texture_size(atlas.texel_size());
program.set_color(plot::Color::hex(0xFF00FF));
atlas.bind();
glyphs.bind();