Skip to content

Introduce the concept of a shared glyph cache#398

Draft
codymullins wants to merge 2 commits into
SixLabors:mainfrom
codymullins:shared-glyph-cache
Draft

Introduce the concept of a shared glyph cache#398
codymullins wants to merge 2 commits into
SixLabors:mainfrom
codymullins:shared-glyph-cache

Conversation

@codymullins
Copy link
Copy Markdown

@codymullins codymullins commented May 22, 2026

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

tl;dr: Adds an opt-in per-canvas glyph-outline cache (Configuration.SetSharedGlyphCache(true)) that reuses glyph outlines across DrawText calls on a canvas, making text-heavy rendering up to ~3× faster and cutting allocations ~2.6× (404 MB → 155 MB at 1000 text runs).

Draft pull request for a possible shared glyph cache. Shows promising performance benchmarks in Starling, especially when rendering an entire screen of text.

Hoping to see if you had appetite for merging in something like this -- it would be great to have. Or if there's anything I'm missing please LMK!

Notes:

  • I tried to find the best way to make it "backwards compatible" since it did seem to slip tolerance a bit for one of the cache tests.
  • The "configuration" route matched an existing pattern and seemed to cause the least downstream effects and not make any breaking changes.

Early benchmarks here, the full suite is still running:
image
image

EDIT - benchmarks from full suite finished:

  ┌──────────┬─────────────────────────┬───────────────────┬───────┬──────────────────────────────┐
  │ RunCount │   Default (per-call)    │ Shared per-canvas │ Ratio │ Allocated (default → shared) │
  ├──────────┼─────────────────────────┼───────────────────┼───────┼──────────────────────────────┤
  │ 50       │ 6.56 ms                 │ 3.25 ms           │ 0.50  │ 20 MB → 10 MB                │
  ├──────────┼─────────────────────────┼───────────────────┼───────┼──────────────────────────────┤
  │ 250      │ 29.74 ms                │ 12.95 ms          │ 0.44  │ 103 MB → 40 MB               │
  ├──────────┼─────────────────────────┼───────────────────┼───────┼──────────────────────────────┤
  │ 1000     │ 139.4 ms (median 117.5) │ 45.04 ms          │ 0.34  │ 404 MB → 155 MB              │
  └──────────┴─────────────────────────┴───────────────────┴───────┴──────────────────────────────┘

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 22, 2026

CLA assistant check
All committers have signed the CLA.

Copy link
Copy Markdown
Member

@JimBobSquarePants JimBobSquarePants left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks fantastic. I wish I had thought of it!

I think we can drop the configuration and use truthy default behaviour. The current differences are imperceptible to the human eye.

For the updated reference image just reuse your new output. I use pinga with lossless optimization to optimize them for storage. https://css-ig.net/pinga

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.

3 participants