Open pthom opened 6 months ago
@pthom After conducting a thorough investigation into the issue, I discovered that a large SVG file (approximately 12MB) from https://github.com/cppfw/svgren/blob/master/tests/unit/samples_data/back.svg takes about 2.5 seconds to load and render using LunaSvg alone. Interestingly, it loads faster than smaller files like glyphs in the repro profile analysis. This suggests that the problem might be associated with FreeType, ImGui, or other components in the reproduction process. Your ongoing collaboration is crucial for improving this library. Thank you for your support!
PS: Happy new year! I'm sorry to bother you on Jan 2nd!
Thanks for the New Year wishes! No bother at all, because clearly, dealing with GitHub issues is the best way to kick off the year. 😜
@sammycage :
You are probably right, there is something sniffy in the various components chain (ImGui => FreeType => LunaSvg).
Here is how I came to this conclusion:
I saw that for example, the glyph number 18 is extremely slow to load.
ImFontAtlasBuildWithFreeTypeEx
(ImGui), when glyph_i==18, just before it calls src_tmp.Font.LoadGlyph()
lunasvg::Document::loadFromData
(a few lines into this function)lunasvg::Document::loadFromData
, I wrote the SVG document with this code: // Write document->svg_document to a file for debugging
std::ofstream file("svg_document.svg");
file.write((const char*)document->svg_document, document->svg_document_length);
And then I looked at the saved svg document for the glyph number 18. And then, surprise, surprise:
@ocornut: what do you think? Is is related to Freetype? Or maybe to ImGui?
I have a suspect inside Freetype, namely ttsvg.c.
More info:
If we look at the evolution of the svg document sizes per glyph, we have:
glyph_i: 0 / 1427
glyph_i: 1 / 1427 document->svg_document_length: 1650
glyph_i: 2 / 1427 document->svg_document_length: 1060
glyph_i: 3 / 1427 document->svg_document_length: 686
glyph_i: 4 / 1427 document->svg_document_length: 534
glyph_i: 5 / 1427 document->svg_document_length: 837
glyph_i: 6 / 1427 document->svg_document_length: 1087
glyph_i: 7 / 1427 document->svg_document_length: 917
glyph_i: 8 / 1427 document->svg_document_length: 922
glyph_i: 9 / 1427 document->svg_document_length: 908
glyph_i: 10 / 1427 document->svg_document_length: 612
glyph_i: 11 / 1427 document->svg_document_length: 941
glyph_i: 12 / 1427 document->svg_document_length: 884
glyph_i: 13 / 1427 document->svg_document_length: 7870
glyph_i: 14 / 1427 document->svg_document_length: 7870
glyph_i: 15 / 1427
glyph_i: 16 / 1427 document->svg_document_length: 14516311
glyph_i: 17 / 1427 document->svg_document_length: 14516311
glyph_i: 18 / 1427 document->svg_document_length: 14516311
glyph_i: 19 / 1427 document->svg_document_length: 4774
glyph_i: 20 / 1427 document->svg_document_length: 14516311
glyph_i: 21 / 1427 document->svg_document_length: 14516311
glyph_i: 22 / 1427 document->svg_document_length: 14516311
glyph_i: 23 / 1427 document->svg_document_length: 14516311
glyph_i: 24 / 1427 document->svg_document_length: 14516311
glyph_i: 25 / 1427 document->svg_document_length: 14516311
glyph_i: 26 / 1427 document->svg_document_length: 14516311
glyph_i: 27 / 1427 document->svg_document_length: 14516311
glyph_i: 28 / 1427 document->svg_document_length: 14516311
glyph_i: 29 / 1427 document->svg_document_length: 14516311
glyph_i: 30 / 1427 document->svg_document_length: 6554
glyph_i: 31 / 1427 document->svg_document_length: 8158
glyph_i: 32 / 1427 document->svg_document_length: 14516311
glyph_i: 33 / 1427 document->svg_document_length: 14516311
...
So, I investigated what happens when glyph_i=16, and somewhere inside the long callstack, we have:
external/freetype/src/sfnt/ttsvg.c, line 283:
FT_LOCAL_DEF( FT_Error )
tt_face_load_svg_doc( FT_GlyphSlot glyph,
FT_UInt glyph_index )
{
FT_Error error = FT_Err_Ok;
TT_Face face = (TT_Face)glyph->face;
FT_Memory memory = face->root.memory;
Svg* svg = (Svg*)face->svg;
FT_Byte* doc_list;
FT_ULong doc_limit;
FT_Byte* doc;
FT_ULong doc_offset;
FT_ULong doc_length;
FT_UShort doc_start_glyph_id;
FT_UShort doc_end_glyph_id;
FT_SVG_Document svg_document = (FT_SVG_Document)glyph->other;
FT_ASSERT( !( svg == NULL ) );
doc_list = svg->svg_doc_list;
error = find_doc( doc_list + 2, svg->num_entries, glyph_index,
&doc_offset, &doc_length,
&doc_start_glyph_id, &doc_end_glyph_id );
And error = find_doc(...)
did set these values:
svg = {Svg *} 0x6000004171c0
version = {FT_UShort} 0
num_entries = {FT_UShort} 674
glyph_index = {FT_UInt} 6
doc_offset = {FT_ULong} 9365
doc_length = {FT_ULong} 14516311 // ARGH.... 14.5MB
doc_start_glyph_id = {FT_UShort} 4. // Glyph number 4...
doc_end_glyph_id = {FT_UShort} 2808. // ... to 2808... Exactly what I saw inside Inkscape
I'm using Freetype VER-2-13-2 (i.e. the latest tag)
And finally, there is a probably related issue for freetype about the same font (NotoColorEmoji):
Freetype does not support 'COLR' v1 tables (whatever that means), but as it appears, it still tries to load fonts with such a format, instead of giving an error.
See https://gitlab.freedesktop.org/freetype/freetype-demos/-/issues/35
Werner Lemberg @wl · 6 months ago Owner
The new NotoColorEmoji.ttf font comes with a 'COLR' v1 table. While FreeType provides routines to read and parse fonts with 'COLR' v1 tables, it has no possibility to actually render them. It is rather simple to provide a simple default rendering for 'COLR' v0, so FreeType has it. However, the version 1 format is far too involved to be handled within FreeType – it is almost as complex as SVG.
If someone provides a library for rendering 'COLR' v1, FreeType can provide hooks in a similar way to SVG. Until then, it is unfortunately not possible to get something better.
I posted the issue to Freetype: https://gitlab.freedesktop.org/freetype/freetype/-/issues/1265
@pthom Well done 👍
Summary of the current situation:
23M NotoColorEmoji-Regular.ttf. // probable issue with Freetype (colored, probably COLR v1)
10M OpenMoji-color-colr0_svg.ttf // probable issue with Freetype (colored, probably COLR v0)
858K NotoEmoji-Regular.ttf // works with Freetype (not colored)
14M TwitterColorEmoji-SVGinOT.ttf // works with Freetype (colored)
39M noto-untouchedsvg.ttf // works with Freetype (colored)
660K seguiemj.ttf // works with Freetype
I can confirm the issue is in Freetype only, since I created a repro that depends only on it. See https://gitlab.freedesktop.org/freetype/freetype/-/issues/1265#note_2226979
If a deep dive into FreeType internals interests you, you can look at https://gitlab.freedesktop.org/freetype/freetype/-/issues/1265#note_2228459
Hello,
I saw some performance issues with ImGui when it uses LunaSvg in order to load some Svg fonts:
It can load some fonts with a very good performance. However, with some others, the performance drops and it takes about 2 seconds per glyph to load.
In order to facilitate the analysis I prepared a repro repository, here: https://github.com/pthom/lunasvg_perf_issue
As explained is the repro repository, a quick analysis with a profiler shows that time seems to be spent in std::map::find + std::vector::emplace_back.
PS: Happy new year! I'm sorry to bother you on Jan 2nd!