Skip to content

Cache shared platform typeface lookups#503

Merged
wieslawsoltes merged 3 commits into
masterfrom
fix/issue-458-shared-typeface-cache
May 9, 2026
Merged

Cache shared platform typeface lookups#503
wieslawsoltes merged 3 commits into
masterfrom
fix/issue-458-shared-typeface-cache

Conversation

@wieslawsoltes
Copy link
Copy Markdown
Owner

PR Summary: Improve repeated Linux font lookup performance

Related Issue

Fixes #458.

Problem

The issue reports a severe slowdown and apparent freezes when loading many SVG files through
Skia.Controls.Avalonia on Linux. The remaining investigation pointed at repeated font and
glyph fallback work, especially after the draw lock removal and clone/reload fixes had already
reduced other sources of contention.

The expensive paths are repeated across SVG loads:

  • resolving a requested family/style through the default platform font manager
  • asking the built-in typeface providers for the same family/style combinations
  • matching a fallback typeface for individual codepoints during text run splitting

These lookups are platform-backed and can be especially expensive on Linux font stacks. The
existing instance-local caches help while one SkiaModel/SkiaSvgAssetLoader instance is reused,
but they do not help the control-like path that repeatedly constructs loader/model instances while
loading many SVGs.

Changes

  • Added SharedTypefaceCache, an internal bounded cache for platform and built-in typeface lookup
    results.
  • Shared only safe, built-in lookup paths:
    • DefaultTypefaceProvider
    • FontManagerTypefaceProvider
    • SKFontManager.Default family/style resolution
    • SKFontManager.Default.MatchCharacter fallback results
  • Kept custom providers instance-specific. Unknown ITypefaceProvider implementations bypass the
    shared cache and still run through the existing instance-local provider cache.
  • Cached both positive and negative platform lookup results so repeated missing-family probes do not
    re-enter the native font manager for every SVG load.
  • Normalized invalid native handles before storing or returning cached entries.
  • Added a regression test proving shared caches do not bypass separate custom provider instances
    across separate SkiaModel instances.

Commit Breakdown

  1. Add shared typeface cache

    • Adds the internal cache helper and key types.
    • Includes cache size caps and invalid-handle checks.
  2. Reuse shared platform font caches

    • Wires shared provider and resolved-family caches into SkiaModel.
    • Wires shared character fallback caching into SkiaSvgAssetLoader.
    • Extracts platform character matching so custom provider lookup stays first.
  3. Cover custom provider cache isolation

    • Adds a focused unit test for custom provider isolation across model instances.

Validation

  • dotnet format src/Svg.Skia/Svg.Skia.csproj --no-restore
  • dotnet format tests/Svg.Skia.UnitTests/Svg.Skia.UnitTests.csproj --no-restore
  • dotnet build Svg.Skia.slnx -c Release --no-restore
  • dotnet test Svg.Skia.slnx -c Release --no-restore --no-build
  • dotnet run --project tests/Svg.Skia.Benchmarks/Svg.Skia.Benchmarks.csproj -c Release --no-restore -- --profile-svg tests/Tests/HitTestText.svg 20

Observed benchmark result for tests/Tests/HitTestText.svg:

  • Control-like source load: mean 0.45 ms
  • Compile retained scene (parsed doc): mean 0.36 ms

Earlier baseline during investigation showed the same benchmark around:

  • Control-like source load: mean 4.14 ms
  • Compile retained scene (parsed doc): mean 2.46 ms

Notes And Risk

  • The original issue sample archive linked from the GitHub discussion returned 404 during
    investigation, so validation used the repository benchmark harness and existing tests.
  • Full solution build passes after restore. The build reports existing NuGet vulnerability warnings
    for packages such as Tmds.DBus.Protocol, NuGet.Packaging, and
    System.Security.Cryptography.Xml; these are unrelated to this font cache change.
  • Shared caches are process-wide and bounded. When a cache exceeds its cap, it is cleared rather than
    growing without limit.
  • The shared provider cache intentionally rejects custom provider types, because custom providers can
    carry mutable or instance-specific font state.

@wieslawsoltes wieslawsoltes merged commit c8a221d into master May 9, 2026
14 checks passed
@wieslawsoltes wieslawsoltes deleted the fix/issue-458-shared-typeface-cache branch May 9, 2026 10:33
@kkmserver
Copy link
Copy Markdown

Hello.

Tested on Svg.Controls.Skia.Avalonia 12.0.0.8
Loading all 450 SVGs in the project:
Windows 0.94 sec.
Linux Ubuntu 2.2 sec. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Thanks a lot!!!

@wieslawsoltes
Copy link
Copy Markdown
Owner Author

Hello.

Tested on Svg.Controls.Skia.Avalonia 12.0.0.8 Loading all 450 SVGs in the project: Windows 0.94 sec. Linux Ubuntu 2.2 sec. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Thanks a lot!!!

Thanks for testing, great that its now much faster on Linux.

@JohnySeven
Copy link
Copy Markdown

Great job Wieslaw! Made loading SVG faster on CM4 1GB RAM version! Full HD SVG with icons loaded in about 1-2 seconds!

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Skia.Controls.Avalonia.11.3.9 package freezes on Linux

3 participants