Skip to content

feat(text): keep MultiFace font fallback on GPU - #513

Merged
kolkov merged 4 commits into
gogpu:mainfrom
besmpl:agent/issue-451-font-fallback
Nov 8, 2026
Merged

kolkov merged 4 commits into
gogpu:mainfrom
besmpl:agent/issue-451-font-fallback

Conversation

@besmpl

@besmpl besmpl commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Source-aware GPU font fallback (#451)

MultiFace now exposes its fallback chain and contiguous source-font runs, and shaping retains the owning face on every ShapedGlyph. The glyph-mask and MSDF GPU paths rasterize those runs into ordered batches instead of returning a nil-source error; shaped CPU fallback also resolves each glyph's source. Mixed-font tests cover deterministic run offsets, source identity, glyph-mask batching, and MSDF batching.

Fixes #521.

Verification: go test ./..., go test -tags nogpu ./..., go vet ./....

@besmpl
besmpl requested a review from kolkov as a code owner August 14, 2026 10:25
@codecov

codecov Bot commented Aug 14, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.98347% with 63 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/gpu/glyph_mask_engine.go 64.40% 23 Missing and 19 partials ⚠️
internal/gpu/gpu_text.go 63.79% 11 Missing and 10 partials ⚠️

📢 Thoughts on this report? Let us know!

@kolkov kolkov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@besmpl, same apology here for the long silence, for the same reason.

Nine days after you opened this, I described the fix in #521 without linking your PR, which already implements it. That was a miss on my side. The direction matches what we planned: split by source face before shaping, keep the owning face on each glyph, and send single-font runs to the glyph-mask and MSDF paths. That replaces our own plan to build it.

I've read the diff, but I haven't run it yet. Its CI ran against the August main, and that run can't be restarted.

Needed before merge:

  1. A rebase onto current main, so CI runs on the code this would merge into. text/ and internal/gpu/ changed since August, and more lands with the next release. If you'd prefer to rebase once after that release, say so and I'll ping you when it's on main.
  2. This adds public API: FontRun, MultiFace.Faces/FaceForRune/FontRuns/ShapeRuns, FilteredFace.FontRuns, text.ShapeRuns and ShapedGlyph.Face. We're freezing the API for v1.0, so each exported name has to be one we'd keep forever. Two questions:
    • Does FontRun.IsCJK need to be exported? It's a policy flag for our atlas and hinting choice. An unexported field, or a script value rather than a bool, would age better.
    • Do we need both MultiFace.ShapeRuns and text.ShapeRuns? The function form already dispatches to FontRuns when the face has no single source, so it covers the method's case.

Optional: ShapedGlyph gains an interface field, 16 bytes per glyph. If you have numbers for a long paragraph through scene before and after, they'd help. I'll measure it myself otherwise.

kolkov added a commit to besmpl/gg that referenced this pull request Nov 8, 2026
gogpu#513 added both MultiFace.ShapeRuns and text.ShapeRuns. The function
already sends a face without a single source to its FontRuns, so it
covers MultiFace; the method had no caller outside its tests. Public
API is frozen at v1.0, so only the function stays. MultiFace.FontRuns
now accepts a nil receiver, which the method used to guard, and the
helper only the method used is gone.

CHANGELOG entry for gogpu#513 credits besmpl.

Refs gogpu#513
@kolkov
kolkov force-pushed the agent/issue-451-font-fallback branch from 61d4d5e to a21515c Compare November 8, 2026 13:46
@kolkov

kolkov commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

@besmpl, same here: I've done the changes on your branch, your authorship kept. Your three commits are rebased onto main without conflicts.

On the two API questions:

  • text.ShapeRuns is the only form now. MultiFace.ShapeRuns is gone. It had no callers outside its tests, and the function already sends a face without a single source to FontRuns. MultiFace.FontRuns now accepts a nil receiver, which the method used to guard.
  • FontRun.IsCJK stays exported. I asked about it before noticing that ShapedGlyph.IsCJK has been public since v0.46.6, and that the GPU code reads the run's flag from another package. Whether both should become a script value instead of a bool is a separate decision we'll make for both types before v1.0.

Cost of ShapedGlyph.Face: the struct goes from 56 to 72 bytes on amd64, the price of a glyph knowing its font.

The CHANGELOG credits you. Thanks for building this.

besmpl and others added 4 commits November 8, 2026 16:52
gogpu#513 added both MultiFace.ShapeRuns and text.ShapeRuns. The function
already sends a face without a single source to its FontRuns, so it
covers MultiFace; the method had no caller outside its tests. Public
API is frozen at v1.0, so only the function stays. MultiFace.FontRuns
now accepts a nil receiver, which the method used to guard, and the
helper only the method used is gone.

CHANGELOG entry for gogpu#513 credits besmpl.

Refs gogpu#513
@kolkov
kolkov force-pushed the agent/issue-451-font-fallback branch from a21515c to 4a8217a Compare November 8, 2026 13:52

@kolkov kolkov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rebased onto current main, CI green, merging for v0.52.7. Your commits stay yours; the one on top drops the duplicate ShapeRuns and the helper only it used.

@kolkov
kolkov merged commit f425bc4 into gogpu:main Nov 8, 2026
14 checks passed
@kolkov kolkov mentioned this pull request Nov 8, 2026
kolkov added a commit that referenced this pull request Nov 8, 2026
…ultiFace architecture note

#540 and #546 shipped without a CHANGELOG entry. The architecture table
still said MultiFace falls back to bitmaps; since #513 each run keeps its
source face and single-font runs stay on the GPU text paths.
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.

No supported way to render text with font fallback on the GPU path

2 participants