rust-wasm:vec< u8>太大,无法增加堆栈大小

hwamh0ep  于 2022-11-30  发布在  其他
关注(0)|答案(1)|浏览(227)

我试图创建一个结构体,它有足够大的数据缓冲区来保存大于64 x 64像素的HTML5画布ImageData。

// src/lib.rs
use wasm_bindgen::prelude::*;
extern crate fixedbitset;
extern crate web_sys;

#[wasm_bindgen]
pub struct CanvasSource {
    width: u32,
    height: u32,
    data: Vec<u8>,
}

#[wasm_bindgen]
impl CanvasSource {
    pub fn width(&self) -> u32 {
        self.width
    }

    pub fn height(&self) -> u32 {
        self.height
    }

    // returns pointer to canvas image data
    pub fn data(&self) -> *const u8 {
        self.data.as_ptr()
    }

    pub fn cover_in_blood(&mut self) {
        let blood: Vec<u8> = vec![252, 3, 27, 255];
        let mut new_data = blood.clone();
        for _ in 0..self.width {
            for _ in 0..self.height {
                let pixel = blood.clone();
                new_data = [new_data, pixel].concat()
            }
        }

        self.data = new_data;
    }

    pub fn new(width: u32, height: u32, initial_data: Vec<u8>) -> CanvasSource {
        let data_size = (width * height) as usize;
        let mut data = initial_data;
        data.resize(data_size, 0);

        CanvasSource {
            width,
            height,
            data,
        }
    }
}

...并在Typescript中从此处调用:

import { useEffect, useRef, useState } from "react";
import { CanvasSource } from "rust-canvas-prototype";
import { memory } from "rust-canvas-prototype/rust_canvas_prototype_bg.wasm";
import styles from "./DirectCanvas.module.css";

const getRenderLoop = (
    source: CanvasSource,
    ctx: CanvasRenderingContext2D,
) => {
    if (source && ctx) {
        const loop = () => {
            const sourceDataPtr = source.data();
            
            const width = source.width();
            const height = source.height();
            const regionSize = width * height * 4;
            
            
            const pixelData = new Uint8ClampedArray(
                memory.buffer,
                sourceDataPtr,
                regionSize
            )

            const imageData = new ImageData(pixelData, width, height);

            ctx.putImageData(imageData, 0, 0)
        };

        return loop;
    }

    return null;
}

export function DirectCanvas() {
    const [source, setSource] = useState<CanvasSource>();
    const [ctx, setCtx] = useState<CanvasRenderingContext2D | null>(null);

    const [paused, setPaused] = useState<boolean>(false);

    // undefined on init, null when paused
    const [animationId, setAnimationId] = useState<number>(0);
    const canvasElement = useRef<HTMLCanvasElement>(null);

    const initialized = source && ctx;

    // initialization
    useEffect(() => {
        if (!source) {
            console.log("loading source");
            let [width, height] = [100, 100];
            // uncomment below to cause error
            // [width, height] = [358, 358]
            setSource(CanvasSource.new(width, height, new Uint8Array([])))
        }

        if (source && !ctx && canvasElement.current) {
            canvasElement.current.height = source.height();
            canvasElement.current.width = source.width();
            setCtx(canvasElement.current.getContext("2d"));
        }
    }, [source, ctx])

    useEffect(() => {
        if (initialized) {
            const renderLoop = getRenderLoop(source, ctx);
            if (renderLoop) {
                renderLoop();
                setTimeout(() => {
                    setAnimationId(prev => prev + 1);
                }, 10)
            }
        }
    }, [source, ctx, animationId]);

    return (
        <div className={styles.Container}>
            <span className={styles.Controls}>
                <button onClick={() => source?.cover_in_blood()}>Splatter</button>
            </span>
            <canvas ref={canvasElement}></canvas>
        </div>
    )
}

该函数在大小为~ 100 x100或更小的情况下正常工作,但一旦总面积开始超过该值,JS将抛出以下错误:

Uncaught RangeError: attempting to construct out-of-bounds Uint8ClampedArray on ArrayBuffer
   loop DirectCanvas.tsx:23
   DirectCanvas DirectCanvas.tsx:77
   ...

Preliminary research暗示Rust端的堆栈大小问题,但尝试在config.toml抛出自己的错误时增加堆栈大小:
= note: rust-lld: error: unknown argument: -Wl,-zstack-size=29491200
如何分配足够大的堆栈大小来绘制大于100 x100的画布?(找到的最小可重现示例here

vmjh9lq9

vmjh9lq91#

哎呀,蛋在我脸上:问题其实不在生 rust 的那一面。
Uint 8Array的默认初始化大小不足以容纳大于~ 100 x100的ImageData。确保CanvasSource被 * 初始化 * 为正确的大小就可以了:

// initialization
    useEffect(() => {
        if (!source) {
            console.log("loading source");
            [width, height] = [1000, 1000]
            // the fixed line: added third argument
            setSource(CanvasSource.new(width, height, new Uint8Array(width * height * 4)))
        }
         ...
    }, [source, ctx])

在生 rust 的一面,我确实有一行代码,我认为可以处理这个:

// src/lib.rs
pub fn new(width: u32, height: u32, initial_data: Vec<u8>) -> CanvasSource {
        let data_size = (width * height) as usize;
        let mut data = initial_data;
        // here
        data.resize(data_size, 0);

        CanvasSource {
            width,
            height,
            data,
        }
    }

但是,很明显,在Rust端调整大小要么(1)没有更新JS要找的空间,要么(2)我误用了Vec.resize()。这两种情况都有可能。谢谢大家指出的东西。

相关问题