5

I'm a total beginner to Rust and WebAssembly. I was trying to find a way to draw on a canvas element with performance in mind.

To draw on a canvas using Rust and WebAssembly I usually find examples in which they would use the browser's CanvasRenderingContext2D interface and draw data on it they will receive from WASM

const canvasContext = canvasElement.getContext("2d");
const canvasImageData = canvasContext.createImageData(width, height);
const imageDataArray = getWasmImageDataArray()
canvasImageData.data.set(imageDataArray);
canvasContext.clearRect(0, 0, width, height);
canvasContext.putImageData(canvasImageData, 0, 0);

Source: https://wasmbyexample.dev/examples/reading-and-writing-graphics/reading-and-writing-graphics.rust.en-us.html

There are variations of this where they would bind the canvas API to Rust and perform the operations in Rust / WebAssembly, but nonetheless, they would always use the CanvasRenderingContext2D API to draw on the canvas.

This of course means there is a roundtrip from Rust / WebAssembly over the Browser's canvas API into the canvas display buffer, which means a drop in performance.

I am wondering if there is another way: Is it possible to bind the buffer for the pixels that the canvas displays directly to Rust and directly manipulate that buffer to change what the canvas shows? Sort of like this (pseudocode)

Rust Pseudocode:

// Makes whole canvas black
drawOnCanvasDisplayBuffer(displayBuffer) {
  for(i = 0; i < displayBuffer.width; i++) {
    for(j = 0; j < displayBuffer.height; j++) {
      displayBuffer[i][j] = COLOR_BLACK
    }
  }
}
Lukas
  • 9,752
  • 15
  • 76
  • 120

1 Answers1

5

WebAssembly has very limited I/O capabilities. The only way it can interact with its host environment (typically the browser) is directly via imported / exported functions or indirectly by linear memory.

The example you cite has a WebAssembly module where its linear memory is shared with the host allowing it to be read and written to by both the WebAssembly module and the JavaScript host. This is an ideal approach for creating an image buffer for canvas operations.

I am wondering if there is another way: Is it possible to bind the buffer for the pixels that the canvas displays directly to Rust and directly manipulate that buffer to change what the canvas shows?

No, it is not. The shared memory instance must be of type WebAssembly.Memory, it cannot be any arbitrary buffer.

Note, in your example code, the clearRect operation isn't needed:

canvasContext.clearRect(0, 0, width, height); // ***
canvasContext.putImageData(canvasImageData, 0, 0)
ColinE
  • 68,894
  • 15
  • 164
  • 232