Open hexaclue opened 11 months ago
What methods have you been comparing?
From my own tests it seems like skia-canvas exports png images quite a bit faster than node-canvas. However, there's no toBuffer('raw') option, and manually converting image data to buffer then saving to file was slightly slower than node-canvas. I wonder if it's possible to have toBuffer export raw pixels, somehow optimizing it in similar ways like png/jpeg exports.
@vincaslt Repeating myself, but it seems relevant here as well -- toImageData()
is synchronous, which is why it appears to be slower (for a canvas with many elements it is a significant difference). The other export options use new threads (from Node thread pool) to produce the final result, so they're not just async but actually multi-threaded as well.
As for the OP question -- personally I think that makes a lot more sense than rendering the whole image every time a new layer/element is added. Typically one doesn't need the final image until it is output, in some fashion, so why bother rendering any intermediate stages? As for comparing with other canvas implementations, I imagine the final result could depend on a number of factors -- actual code/benchmark would be most relevant here.
I'm not sure if it's relevant for the issue of the topic starter, but I saw the notion of "5 fps", so it might be the case.
I was drawing a lot of frames for a video by just reusing the same canvas. Each frame gets exported, and the next frame gets drawn on top of the previous one(s). And eventually I noticed that exporting the first frames was times (tens of times) faster than exporting the last ones. That was weird. And then I realized.
If you think of the canvas as of a raster pixel matrix, then there's no issue with this approach. Draw -> export -> rewrite on top -> export - must be OK. But once you learn it's all vector, it's a completely different story. It turns out I was eventually adding thousands of complex objects into the same canvas without ever clearing them. Then it totally makes sense that computing what must actually be visible in the end raster image takes longer and longer and longer the more objects you add.
TLDR the solution was strikingly simple. Just ctx.reset()
at the beginning of each new frame drawing procedure, and suddenly the frame generation process became linear in performance, in my case approximately 30fps (0.98 jpeg quality).
Hope this helps.
EDIT: My thinking of skia-canvas
as a vector canvas might be wrong of course, but the result seems to prove that it collects some internal state over time, and that state takes very long to render once it's grown big.
Using any of the methods to save the canvas to a file is extremely slow compared to node-canvas.
My render loop would take an average speed of 20-30 fps using node-canvas, whereas skia-canvas takes almost four times as long with only 5 fps (sometimes even lower, like 2).
Removing any of
canvas.saveAs
,canvas.toBuffer
orcanvas.toDataURLSync
from my skia-canvas-using code speeds up the render loop to over 1400 fps, indicating the problem definitely lays in the exporting of the canvas. Even when using justcanvas.toBuffer
without writing to the disk slows the render loop down.The performance drop is observed to be the same, no matter what format ("jpg", "png") is used or whether
canvas.gpu
is enabled or not.