processing / p5.js

p5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is based on the core principles of Processing. http://twitter.com/p5xjs —
http://p5js.org/
GNU Lesser General Public License v2.1
21.53k stars 3.3k forks source link

Slower rendering of custom fonts (.otf, .ttf, etc) #3435

Open dhowe opened 5 years ago

dhowe commented 5 years ago

It has been noted that there is a significant performance hit when using a custom font (.otf or .ttf) vs the default or other browser-fonts (see this post for one example). I've looked at the p5.js text rendering code and I don't think (others are welcome to chime in) there is much that can be done in terms of optimization here, as it largely delegates to opentype.js for the computation (and creation) of paths and then simply renders them with primitive canvas ops. There may well be optimizations that can be done within opentype, but this is a bigger undertaking.

Alternatively we may see some performance gains via the caching of paths returned from opentype, but this would likely not help (or not help much) with the sketch linked above as the characters change each frame. However, this sketch is probably not a very typical use case for p5, as most text will remain in place for multiple frames, thus recomputing the path data (and re-creating Path objects) is not optimal. The tradeoff would likely be a memory hit, but tests would be needed to see how significant this would be. I'm happy to look into this (though probably not right away) if people think it is worthwhile. Mostly I wanted to log this as a known issue.

dhowe commented 5 years ago

After a bit of profiling it seems that, as expected, most of the rendering time is spent on canvas drawing/path primitives. Unexpectedly, however, _handleAlignment appears to be more costly than getPath

image

Some quick experiments with alignment and path caching (basically memoizing these two functions) raises the framerate of my test case (modified from the above test so that characters are reused across frames) by about 30%. Non-scientific numbers on time-spent below (from these estimates, it may not be worth the complexity/memory hit)...

current code: _renderPath: 50.4%, _getPath: 26.5% 
memoizing alignments: _renderPath: 42.7%, _getPath: 15.2% 
memoizing paths & alignments: _renderPath: 37.0%, _getPath:  8.9% 

another option might be to draw text paths to an offscreen buffer, which can apparently speed things up significantly (perhaps an order of magnitude), but has all the usual problems of offscreen buffers

Spongman commented 5 years ago

while it won't help in all cases, for this particular example, the following additional check knocks ~25% off the running time:

https://github.com/processing/p5.js/blob/edb41cf513efafd3734221a3b8cdc266f90d6bb3/src/typography/p5.Font.js#L448

to:

  if (typeof textWidth === 'undefined' &&
    renderer._textAlign !== constants.LEFT) {
    textWidth = this._textWidth(line, fontSize);
  }

when the _textWidth call is unavoidable, i think it's a lost opportunity that the getPath call doesn't return the accumulated advanceWidth somehow. this could be used instead of having to re-calculate exactly the same value in order to do the right/center justification.

lmccart commented 5 years ago

@dhowe is this the same issue as https://github.com/processing/p5.js/issues/950? can we close one of them?

MirTunio commented 1 year ago

Hey folks, running into serious fps issues rendering ttf font, 40+ FPS using default p5js font: image

And only 6 FPS with VCR_OSD_MONO_1.001.ttf: image

Also, I don't know how to begin searching for less fonts with less complex "font-paths" in case I need to live with this.

limzykenneth commented 1 year ago

I would imagine finding fonts with less complex font paths will be difficult as most of them will be relatively complex. The suggestions of drawing to an offscreen buffer (ie. p5.Graphics) may work depending on your use case, if you are drawing a lot of the same text it should help, if you are modifying the text often unpredictably then it will likely have the same issue.

One way to possibly get performance of custom fonts closer to built in fonts would be to load them via CSS and access them via font name. I think @munusshih looked into this but can't at this moment remember what the conclusion was.