Poor performance for `<Resource>` fonts
See original GitHub issue- .NET Version: 6.0
- Windows version: Windows 10 21H2 (19044.1526)
- Does the bug reproduce also in WPF for .NET Framework 4.8?: Yes
Problem description:
When using a font that is embedded as a <Resource>
(see fonts as resource items), the performance of rendering text can be extremely poor.
This was first noted in a production .NET 4.8 WPF Application; a detailed writeup of the problem and a solution that was found is at https://faithlife.codes/blog/2019/06/improving-wpf-text-display-performance/.
Actual behavior:
dotTrace shows that very large amounts of time are spent in MS.Internal.Text.TextInterface.FontFileStream.ReadFileFragment
. Additionally, large amounts of memory are allocated and the program spends a lot of time in GC.
Expected behavior:
Rendering a <Resource>
font is extremely efficient because the resource is already mmap’ed into memory and returning its data could be as simple as a pointer addition.
Minimal repro:
An example repro is at https://github.com/bgrainger/EmbeddedFontPerformance. Clone the repo, build the code, and scroll the text box. You’ll notice that it can take several seconds to draw the next screen of text (depending on how large the window is).
The following text complements that repo and describes how it reproduces the problem:
This reproduces most easily with the Noto Sans CJK font (perhaps because of the large number of glyphs that have to be read?).
Embed the font:
<ItemGroup>
<Resource Include="NotoSansCJKtc-Regular.otf" />
</ItemGroup>
Then create a text box that references the font and contains a large amount of Chinese text:
<Grid>
<ScrollViewer>
<TextBlock FontFamily="./#Noto Sans CJK TC" TextWrapping="Wrap">
<!-- place Chinese text here -->
</TextBlock>
</ScrollViewer>
</Grid>
Run the app and scroll the ScrollViewer.
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (8 by maintainers)
Top GitHub Comments
@bgrainger send PR to C++ part. I think that way we can hedge if DWrite rewrite in C# get stalled indefinitely. Also if you can measure improvements somehow that helps advocate for the fix
As long as the array isn’t deallocated before
ReleaseFileFragment
is called, there is no risk. AFAIK, theUnmanagedMemoryStream
returned for resources is a pointer to the resource within the DLL that’s loaded into the process’ address space, so this can’t happen.As per the blog post in the OP, a “hack” that implements this solution has been shipping (with no problems) in Logos for years, so I feel confident in this solution.