use wasm_bindgen::{prelude::*, Clamped};
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, ImageData};
// Decreasing WIDTH or HEIGHT to 511 or below will make this work
const WIDTH: usize = 512;
const HEIGHT: usize = 512;
#[wasm_bindgen(start)]
pub fn start() -> Result<(), JsValue> {
let document = web_sys::window().unwrap().document().unwrap();
let canvas: HtmlCanvasElement = document
.get_element_by_id("canvas")
.unwrap()
.dyn_into()
.unwrap();
canvas.set_width(WIDTH as u32);
canvas.set_height(HEIGHT as u32);
let context: CanvasRenderingContext2d = canvas
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into()
.unwrap();
let mut pixels: [u8; WIDTH * HEIGHT * 4] = [255; WIDTH * HEIGHT * 4];
// Fill the pixels array with red
for y in 0..HEIGHT {
for x in 0..WIDTH {
let index = (y * WIDTH + x) * 4;
if let Some(pixel) = pixels.get_mut(index..index + 4) {
pixel[0] = 255;
pixel[1] = 0;
pixel[2] = 0;
pixel[3] = 255;
let clamped_buf = Clamped(pixels.as_ref());
// This will fail with RuntimeError: memory access out of bounds
let image_data = ImageData::new_with_u8_clamped_array(clamped_buf, WIDTH as u32)?;
context.put_image_data(&image_data, 0.0, 0.0)?;
Ok(())
Expected Behavior
ImageData::new_with_u8_clamped_array()
and ImageData::new_with_u8_clamped_array_and_sh()
should work with arbitrarily sized data
.
Actual Behavior
If the provided data
exceeds a certain size (in my case 512 * 512 * 4
elements) a RuntimeError: memory access out of bounds
will be thrown.
Additional Context
Tested with wasm-bindgen = "0.2.84"
and js-sys = "0.3.61"
.
Looks like you've run into an odd form of stack overflow.
lld
only allocates 1MiB for the WebAssembly stack, and the array that you're allocating on the stack is exactly 1MiB. In combination with the other stuff on the stack, that's causing a stack overflow.
The easiest solution would just be to heap allocate it, with a Vec<u8>
or Box<[u8; WIDTH * HEIGHT * 4]>
.
If you want, you can also pass a flag to lld
to increase the stack size, e.g. with this build.rs
:
fn main() {
// 0x200000 bytes = 2MiB
println!("cargo:rustc-link-arg=-zstack-size=0x200000")
Thanks @Liamolucko, I wasn't aware of the limited stack size. Using a Vec<u8>
solved the issue and seems to be a more sustainable and safe solution compared to increasing the stack size:
let mut pixels: Vec<u8> = vec![255; WIDTH * HEIGHT * 4];