Skip to content

perf: glyph texture atlas to avoid redundant rasterisation in GL text rendering#6390

Open
AJ0070 wants to merge 3 commits into
fyne-io:developfrom
AJ0070:fix/performance-issues
Open

perf: glyph texture atlas to avoid redundant rasterisation in GL text rendering#6390
AJ0070 wants to merge 3 commits into
fyne-io:developfrom
AJ0070:fix/performance-issues

Conversation

@AJ0070

@AJ0070 AJ0070 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Description:

Improve Android (and general GL) text rendering performance by introducing a glyph-level bitmap cache (a "glyph texture atlas") that avoids re-rasterising the same glyph shape every time it appears in a new string.

The root cause of the slowdown was that the existing cache keyed on the full rendered string: every unique (text, size, style, colour, canvas) tuple produced a fresh HarfBuzz shaping pass, freetype rasterisation, and TexImage2D upload. In list or grid views with hundreds of distinct strings sharing common characters (digits, punctuation, repeated words), this meant the same glyph outlines were converted to pixels repeatedly on every paint cycle.

This change adds a process-wide glyphAtlas (in internal/painter/glyph_atlas.go) that caches pre-rendered *image.RGBA bitmaps keyed on (font face, GlyphID, quantised pixel size, RGBA colour). Two new functions, DrawStringAtlas and DrawStringOffsetAtlas, replace DrawString/DrawStringOffset in the GL texture path: they walk the HarfBuzz-shaped glyph runs, retrieve or rasterise each glyph's bitmap from the atlas, and blit them into the destination image with draw.Draw. HarfBuzz kerning offsets (XOffset/YOffset) are excluded from the cache key and applied at blit time, so kerned pairs reuse the same atlas entry as their unkerned counterparts. ClearFontCache is extended to also flush the glyph atlas on theme or font changes.

No shader changes, no new GPU memory layout, and no public API surface changes are required.

Checklist:

  • Tests included.
  • Lint and formatter run with no errors.
  • Tests all pass.

Part of #1062

@AJ0070 AJ0070 changed the title perf: glyph texture atlas to avoid redundant rasterisation in GL text… perf: glyph texture atlas to avoid redundant rasterisation in GL text rendering Jun 30, 2026
@coveralls

coveralls commented Jun 30, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 59.765% (-0.1%) from 59.869% — AJ0070:fix/performance-issues into fyne-io:develop

@andydotxyz

Copy link
Copy Markdown
Member

Thanks for working on this, however I think that this approach may not have the result you assert (have you done any performance testing before/after?). As I understand it this adds a new cache of bitmaps in memory to aid in building the string bitmaps faster, however it does not upload any fewer textures to the GPU and so we have no improvement in the graphics pipeline.

From the issue:

moving to glyph based render cache instead of per-text-object caches

I appreciate this is not a strong brief but it shows the desire to remove per-text-object image caches which we still have here.

If you can demonstrate through metrics or benchmarks that this delivers the desired speed improvements then that's great but from reading it through I'd be surprised.

For more context, referring to your description:

every unique (text, size, style, colour, canvas) tuple produced a fresh HarfBuzz shaping pass, freetype rasterisation, and TexImage2D upload. In list or grid views with hundreds of distinct strings sharing common characters (digits, punctuation, repeated words), this meant the same glyph outlines were converted to pixels repeatedly on every paint cycle.

The TexImage2D upload you refer to is still here, but the "repeatedly on every paint cycle" may need some understanding - we only repaint strings when they have changed (our existing texture cache), so the bottleneck appears to be the high number of textures created/uploaded.

As I said above if you can demonstrate this is the performance boost we were looking for then that's great - but keeping the GPU render pipeline unchanged makes it seem unlikely.

@andydotxyz

Copy link
Copy Markdown
Member

p.s. Please don't tick "tests included" on a PR with no tests.

@andydotxyz

Copy link
Copy Markdown
Member

I just had a thought - if this improvement genuinely is a real boost to performance, then perhaps we should open the PR on go-text/render instead? That way the code is closer to the actual render pipeline... and others get the benefit too.

Let me know what you think - the glyph atlas approach is very powerful and I am keen to see such a thing land.

@redawl

redawl commented Jul 4, 2026

Copy link
Copy Markdown
Member

I just had a thought - if this improvement genuinely is a real boost to performance, then perhaps we should open the PR on go-text/render instead? That way the code is closer to the actual render pipeline... and others get the benefit too.

Let me know what you think - the glyph atlas approach is very powerful and I am keen to see such a thing land.

From my testing there is not much of an improvement (if any) with this PR.

IMO it would be good to contribute a gl renderer to go-text/render, which could then use glyph based rendering.

The only difficulty is the amount of deps that are needed for gl. go-gl, gomobile, custom windows gl loading, etc.

@andydotxyz

Copy link
Copy Markdown
Member

I just had a thought - if this improvement genuinely is a real boost to performance, then perhaps we should open the PR on go-text/render instead? That way the code is closer to the actual render pipeline... and others get the benefit too.
Let me know what you think - the glyph atlas approach is very powerful and I am keen to see such a thing land.

From my testing there is not much of an improvement (if any) with this PR.

IMO it would be good to contribute a gl renderer to go-text/render, which could then use glyph based rendering.

The only difficulty is the amount of deps that are needed for gl. go-gl, gomobile, custom windows gl loading, etc.

I looked into this too and the CPU based caching in this PR is heading in the right direction but as you say is not much of a saving in reality. The texture atlas really does need to be pushed to the GPU so we can just upload lists of glyph IDs and positioning info to the GPU - that is where the real saving should be.
I think I mentioned above - but texture upload is the main bottleneck we are working with now and that would likely offer the true win indicated by the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants