Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drawImage performance bug with canvas #972

Open
monteslu opened this issue Jan 13, 2025 · 1 comment
Open

drawImage performance bug with canvas #972

monteslu opened this issue Jan 13, 2025 · 1 comment

Comments

@monteslu
Copy link

Drawing a canvas onto a canvas is almost 8 times slower than drawing an image onto a canvas:

import { createCanvas, loadImage } from '@napi-rs/canvas';


const canvas = createCanvas(1920, 1080);
const ctx = canvas.getContext('2d');

const iterations = 100;

async function main() {

  const image = await loadImage('./640x480_image.png');

  const canvas2 = createCanvas(640, 480);
  const ctx2 = canvas2.getContext('2d');
  ctx2.fillStyle = 'white';
  ctx2.fillRect(0, 0, canvas2.width, canvas2.height);

  let start = performance.now();
  for (let i = 0; i < iterations; i++) {
    ctx2.drawImage(image, 0, 0, canvas.width, canvas.height);
  }
  let end = performance.now();
  console.log('average draw image time', (end - start) / iterations);

  start = performance.now();
  for (let i = 0; i < iterations; i++) {
    ctx.drawImage(canvas2, 0, 0, canvas.width, canvas.height);
  }
  end = performance.now();
  console.log('average draw canvas time', (end - start) / iterations);
}

main();

outputs:
average draw image time 1.2156541699999999
average draw canvas time 8.897838329999999

On the web the two draw in about the same amount of time. 7 milliseconds can make a huge amount of difference when you might have a 16 millisecond budget rendering at 60Hz.

My stab in the dark guess is that somewhere along the way there's a mem copy happening.
Something similar to: Buffer.from(imageData.data.buffer)) vs Buffer.from(imageData.data))

@monteslu
Copy link
Author

ran benchmark on my mac pro 14 M2 Max 64G

Draw a House and export to PNG
┌─────────┬─────────────────┬───────────────────────┬──────────────────────────┬────────────────────────────┬───────────────────────────┬─────────┐
│ (index) │ Task name       │ Latency average (ns)  │ Latency median (ns)      │ Throughput average (ops/s) │ Throughput median (ops/s) │ Samples │
├─────────┼─────────────────┼───────────────────────┼──────────────────────────┼────────────────────────────┼───────────────────────────┼─────────┤
│ 0       │ '@napi-rs/skia' │ '19647577.48 ± 1.72%' │ '19294625.00 ± 26708.00' │ '51 ± 1.68%'               │ '52'                      │ 64      │
│ 1       │ 'skia-canvas'   │ '19991857.44 ± 2.72%' │ '19301208.50 ± 10708.50' │ '50 ± 1.80%'               │ '52'                      │ 64      │
│ 2       │ 'node-canvas'   │ '22529859.37 ± 1.52%' │ '21699937.50 ± 8062.50'  │ '45 ± 1.43%'               │ '46'                      │ 64      │
└─────────┴─────────────────┴───────────────────────┴──────────────────────────┴────────────────────────────┴───────────────────────────┴─────────┘
Draw Gradient and export to PNG
┌─────────┬─────────────────┬───────────────────────┬──────────────────────────┬────────────────────────────┬───────────────────────────┬─────────┐
│ (index) │ Task name       │ Latency average (ns)  │ Latency median (ns)      │ Throughput average (ops/s) │ Throughput median (ops/s) │ Samples │
├─────────┼─────────────────┼───────────────────────┼──────────────────────────┼────────────────────────────┼───────────────────────────┼─────────┤
│ 0       │ '@napi-rs/skia' │ '20375752.00 ± 1.57%' │ '19875125.00 ± 17542.00' │ '49 ± 1.50%'               │ '50'                      │ 64      │
│ 1       │ 'skia-canvas'   │ '21162263.69 ± 1.37%' │ '20728291.50 ± 9666.50'  │ '47 ± 1.32%'               │ '48'                      │ 64      │
│ 2       │ 'node-canvas'   │ '24238384.72 ± 1.20%' │ '24197770.50 ± 23062.50' │ '41 ± 1.19%'               │ '41'                      │ 64      │
└─────────┴─────────────────┴───────────────────────┴──────────────────────────┴────────────────────────────┴───────────────────────────┴─────────┘

then added a canvas on canvas draw to both tests:

// start of drawGradient() and drawHouse()
 const canvas = factory(1024, 768)
 const canvas2 = factory(1920, 1080)

 const ctx = canvas.getContext('2d')!
 const ctx2 = canvas2.getContext('2d')!

// the test's  canvas drawing commands


// draw the canvas on a canvas
ctx2.drawImage(canvas, 0, 0, canvas2.width, canvas2.height)

// rest of test
Draw a House and export to PNG
┌─────────┬─────────────────┬───────────────────────┬──────────────────────────┬────────────────────────────┬───────────────────────────┬─────────┐
│ (index) │ Task name       │ Latency average (ns)  │ Latency median (ns)      │ Throughput average (ops/s) │ Throughput median (ops/s) │ Samples │
├─────────┼─────────────────┼───────────────────────┼──────────────────────────┼────────────────────────────┼───────────────────────────┼─────────┤
│ 0       │ '@napi-rs/skia' │ '29897975.89 ± 1.59%' │ '29156979.00 ± 12896.00' │ '34 ± 1.52%'               │ '34'                      │ 64      │
│ 1       │ 'skia-canvas'   │ '19575501.30 ± 1.22%' │ '19075167.00 ± 1917.00'  │ '51 ± 1.13%'               │ '52'                      │ 64      │
│ 2       │ 'node-canvas'   │ '41313487.00 ± 1.88%' │ '40894791.50 ± 50958.50' │ '24 ± 1.78%'               │ '24'                      │ 64      │
└─────────┴─────────────────┴───────────────────────┴──────────────────────────┴────────────────────────────┴───────────────────────────┴─────────┘
Draw Gradient and export to PNG
┌─────────┬─────────────────┬───────────────────────┬──────────────────────────┬────────────────────────────┬───────────────────────────┬─────────┐
│ (index) │ Task name       │ Latency average (ns)  │ Latency median (ns)      │ Throughput average (ops/s) │ Throughput median (ops/s) │ Samples │
├─────────┼─────────────────┼───────────────────────┼──────────────────────────┼────────────────────────────┼───────────────────────────┼─────────┤
│ 0       │ '@napi-rs/skia' │ '32166183.55 ± 1.70%' │ '31983208.50 ± 6541.50'  │ '31 ± 1.70%'               │ '31'                      │ 64      │
│ 1       │ 'skia-canvas'   │ '20386656.27 ± 1.14%' │ '19968645.50 ± 145.50'   │ '49 ± 1.04%'               │ '50'                      │ 64      │
│ 2       │ 'node-canvas'   │ '41585669.28 ± 1.81%' │ '40705791.50 ± 63541.50' │ '24 ± 1.68%'               │ '25'                      │ 64      │
└─────────┴─────────────────┴───────────────────────┴──────────────────────────┴────────────────────────────┴───────────────────────────┴─────────┘

drops to about 62 to 65 % of skia-canvas performance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant