Skip to content

[Performance] Direct rendering to OffscreenCanvas(es) #360

@djahandarie

Description

@djahandarie

Hi! I'm using resvg-js in wasm in the browser (in a Chrome extension, yomitan).

I use it basically like this:

const opts = {
    fitTo: {
        mode: 'width',
        value: m.width,
    },
};
const resvgJS = new Resvg(new Uint8Array(m.content), opts);
const pngData = resvgJS.render();
const pngBuffer = pngData.asPng();

const drawPromises = [];
const imageDecoder = new ImageDecoder({type: 'image/png', data: pngBuffer});
drawPromises.push(imageDecoder.decode().then((decodedImage) => {
    return {canvases: m.canvases, image: decodedImage.image};
}));
for (const {canvases, image} of await Promise.all(drawPromises)) {
    for (const c of canvases) {
        c.getContext('2d')?.drawImage(image, 0, 0, c.width, c.height);
    }
}

(Note: we Promise.all the decode promises in order to have a very tight drawImage loop)

Basically, we need to render a large number of SVGs in a limited amount of time budget, which we currently cannot meet, and we are observing the following:

  • Almost 60% of a given render is spent in asPng, and 40% in render for resvg, suggesting a potential 2x speedup
  • Even ignoring the numbers, we are clearly doing something very inefficient here by encoding to PNG and then immediately decoding it in order to draw to the canvas(es)

I could see any of the following resvg-js changes potentially helping:

  1. Return a bitmap instead of a png. This might do the trick by itself, but there might be a bit of overhead from the much larger uncompressed bitmap being sent across the wasm-js boundary
  2. Have resvg-js support drawing directly to a single OffscreenCanvas, and then transfer that canvas back to the caller so we can further copy it to our other canvases with drawImage
  3. Have resvg-js support drawing directly to multiple OffscreenCanvas. This would be the most ideal API for us but maybe a bit too overspecialized? Not sure how many other people would need this feature of being able to write the same SVG to multiple canvases.

We would prefer it in order of 3, 2, 1. But ease of implementation is probably in order of 1, 2, 3. And 3 is probably a bit too niche (but don't let me stop you if you think it seems good as it's our ideal...).

What do you folks think? Do any of these options seem appropriate / feasible to include in the library?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions