canvaskit: paragraph.unresolvedCodepoints() return empty list if using cached paragraph builder

129 views
Skip to first unread message

Alan Song

unread,
Apr 9, 2024, 9:53:23 PMApr 9
to skia-discuss

Not sure if this is could be considered a bug - If multiple paragraphs are built with the same builder with `builder.reset()`s in between, only the first paragraph's `unresolvedCodepoints()` call will return results, and any other paragraphs built with the same builder will return an empty list.

Reproduction: https://jsfiddle.net/cexfLsry/2/ 

builder-unresolved-code-points.png

Using a fresh builder solves the issue. Removing `builder.reset()` also makes `unresolvedCodepoints()` work, but we need `reset()` to reuse builder for different paragraphs.

We're caching the builders because the creation overhead adds up when rendering large amount of rich text, which can be easily eliminated by simply caching 3 builders of start/center/end align.


jlav...@google.com

unread,
Apr 22, 2024, 10:27:26 AMApr 22
to skia-discuss
Thank you, yes, it's a bug. We need to cache this info, too.
There is one problem: my results are slightly different from yours.
As I expected, SkParagraph does not cache unresolvedCodepoints so whenever you reuse the cached copy 
you will get the empty set. In C++ however fresh builder does not change it (and it should not!).
SkParagraph finds and reuses the same copy of the shaped text regardless of the builder.
So I think there is a problem with CanvasKit, too. First, I will fix the caching issue then we can deal with CanvasKit
(it does not find the cached copy for different builder and it can cause performance problems).

Alan Song

unread,
Apr 22, 2024, 10:44:52 PMApr 22
to skia-discuss
Thank you very much for taking time to look into this. Looking forward to your fix and let me know if there's anything I can help with (e.g. testing)! 

> SkParagraph finds and reuses the same copy of the shaped text regardless of the builder.

Does it mean SkParagraph utilizes another global cache, if not scoped to the builder? Could it potentially cause memory leak issue like  https://groups.google.com/g/skia-discuss/c/1Ojlla6O6vQ ?

jlav...@google.com

unread,
Apr 23, 2024, 10:09:24 AMApr 23
to skia-discuss
The builder is just a helper to create a paragraph. No connection to the created paragraph, no cache there.
SkParagraph is using a global cache scoped to FontCollection. That cache keeps the results of shaping - the most time-consuming text layout operation.
In theory you can consider any cache as a "memory leak". 
Unfortunately, the reproduction link does not work in the issue you are referring to so I cannot tell if every paragraph there requires another cache position.
(In general, text change, font name/size/type change, font variations, ... - they cause the text shaping result to change so we have another value cached, and memory increased).
You can easily check if it's the case by doing  fontCollection->getParagraphCache()->turnOn(false);

Alan Song

unread,
Apr 24, 2024, 3:28:57 PMApr 24
to skia-discuss

Thank you for the detailed explanation, now it makes much more sense to me. 

May I ask what are the issues with the reproduction links and how may I correct them?  

  • For the unresolvedCodepoints() - you mentioned your results are slightly different. Is it a difference between the wasm build and native skia? I tend to think it’s unlikely the jsfiddle link https://jsfiddle.net/cexfLsry/2/ would show different results on different machines. 
  • For the memory leak issue with fontVariation - I updated the description in the original thread. Basically the test adds a builder and lays out a paragraph, and deletes both, in a loop that runs 500 times. Since we’re using same small input (“test”) and deleting everything afterwards, the js heap size should not increase, but the result is thta js heap size grows with the number of iterations. In case the GitHub link doesn’t work I copied the script content into the thread. 

Thank you!

Reply all
Reply to author
Forward
0 new messages