0

Inside the excellent WasmFiddle tool, I'm using this WASM function:

int f(char *in, char *out, int len) { 
    for (int i = 0; i < len; i++) {
        out[4*i] = in[i];
        out[4*i+1] = in[i];
        out[4*i+2] = in[i];
        out[4*i+3] = 255;
    }   
    return 0;
}

and this JS code:

var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, wasmImports);
var width = 1000, height = 1000;
var array = new Uint8Array(width*height).fill().map(() => Math.round(Math.random() * 255)); // random values
var rgba = new Uint8ClampedArray(4*width*height);
wasmInstance.exports.f(array, rgba, width*height);

The last line is an error because it fails to pass the reference/pointer of array and rgba.

How to do this, in particular in this WasmFiddle environment?

I've read Pass array to C function with emscripten, How to handle passing/returning array pointers to emscripten compiled code?, Pass a JavaScript array as argument to a WebAssembly function but I don't see how to apply this here.


Attempt #1 inspired from Passing arrays between wasm and JavaScript:

var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, wasmImports);
var width = 10, height = 10;
var array = new Uint8Array(wasmInstance.exports.memory.buffer, 0, width*height).fill().map(() => Math.round(Math.random() * 255)); // random values
var rgba = new Uint8ClampedArray(wasmInstance.exports.memory.buffer, width*height, 4*width*height);
wasmInstance.exports.f(array.byteOffset, rgba.byteOffset, width*height);
log(array);
log(rgba);

but in the result we see [0, 0, 0, 255, 0, 0, 0, 255, ...]: other values haven't been modified as expected. Also when increasing the size to width and height = 1000, it doesn't work anymore.

1
  • Note: The attempt #1 fails with line 4: Uncaught RangeError: Invalid typed array length: 1000000 when taking height=width=1000. Maybe the problem is that we don't pass a pointer to WASM, here it's the contrary: we are expecting WASM to allocate the memory, and we take it from WASM in JS. We should do the contrary: build the object into JS and pass a pointer to WASM. How to do that? Commented Aug 8, 2022 at 21:54

1 Answer 1

0

See MDN's documentation for TypedArray.map():

map() does not mutate the typed array on which it is called (although callbackFn, if invoked, may do so).

So, this makes a new typed array rather than modifying the underlying data.

The correct code would be:

var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, wasmImports);
var width = 10, height = 10;
var array = new Uint8Array(wasmInstance.exports.memory.buffer, 0, width*height).fill();
array.forEach((v, i, a) => { a[i] = Math.round(Math.random() * 255); }); // random values
var rgba = new Uint8ClampedArray(wasmInstance.exports.memory.buffer, width*height, 4*width*height);
wasmInstance.exports.f(array.byteOffset, rgba.byteOffset, width*height);
log(array);
log(rgba);

As for memory size, you have a few options.

Essentially, you can either let the WASM create the memory and export it, or let JS create the memory and import it.

LLVM's LLD WebAssembly docs explain you can use the linker flags --initial-memory=<value> and --max-memory=<value> to set the initial and maximum memory size from the linker (in 64KiB pages).

Otherwise, to set the memory from JS, you would use the flag --import-memory, and construct a WebAssembly.Memory object using initial/maximum memory sizes.

It seems WasmFiddle does not support linker flags, and the linker defaults to exporting the memory, so your only option is to grow the existing memory.

// Grow WASM memory by amount used (in 64KiB pages)
wasmInstance.exports.memory.grow((width*height + width*height*4) >> 16);

This seems to cause strange freezing in my browser - I suspect this is a bug in the editor, so try it in another context to see if it has the same issues.

Sign up to request clarification or add additional context in comments.

10 Comments

Thanks! One problem solved! But it seems it still doesn't work with width=height=1000, line 4: Uncaught RangeError: Invalid typed array length: 1000000. How to do this? Maybe the problem is that we don't pass a pointer to WASM, here it's the contrary: we are expecting WASM to allocate the memory, and we take it from WASM in JS. We should do the contrary: build the object into JS and pass a pointer to WASM. How to do that?
Have a look at this example, it shows how to allocate your own memory with an arbitrary size: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
thanks, feel free to update your answer with this, then I can accept it.
I have updated it, there are no great solutions because the tool you are using doesn't seem to offer many advanced features but if you deploy this on your own code you should be able to use these options
Thanks @AlexApps99. I prefer to create variables on JS side, for example var rgba = new Uint8ClampedArray(4*width*height);. If I don't use WasmFiddle but a real compiler, could you include the compilation command we should use + the code to call the WASM and pass access to rgba?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.