[SOLVED] Load wasm from project assets

Hello,

I was trying to load a wasm that is part of the project. Unfortunately, it didn’t work, giving an error when the loader is trying to locate the binary in the Window global space during loadWasmModuleAsync(). Something about lib is not a function. I think the Window.mylib is undefined.

I originally wrote the script in Typescript and compiled to wasm binary using AssemblyScript. However, it doesn’t auto-generate the glue script, like Emscripten does. It has own loader, which I don’t know know how to plug into Playcanvas. I then wrote it in C++ and compiled with Emscripten, then assgined the glue file to the wasm property, gave it a name aaaand… it didn’t work. It gives me the error I mentioned in the beginning. I also tried to write it in Rust and compile using wasm-pack, but that generates a module package, which is not working with browsers natively, and I didn’t try to browserify it. I feel like Emscripten gave me the output that was the closest to what Playcanvas expects? I’d prefer assembly script, though, or rust, since they generally produce binaries with smaller sizes.

I asked the question in the Discord originally, but decided to put it here, for reference. What would be the right way to load own WASM binaries which are part of the project assets?

Edit: I was following this manual

maybe have a look at how wasm loader works for the viewer:

it assigns the library module to the window

window[moduleName + 'Lib'] = lib;

and then we use it like this (example - draco wasm in glb-parser.js)

                var decoderModule = window.DracoDecoderModule;

1 Like

Thank you for the direction, @mvaligursky!

I actually saw this example, but couldn’t make it to work, because it uses a helper to resolve the path to the wasm:

I think that __PUBLIC_PATH__ is set during the build time. That path is then given to loadWasmModuleAsync() which loads it. This works if I do an engine-only build.

But how do I resolve the file path from the project in the editor? Or what url do I give to the loader?

I will try to use a custom loader, and will post here.

I do something similar, load WASM modules from assets and the regular pc.Asset getFileUrl() method works quite ok with the loader:

    const glueAsset = this.app.assets.find(module.glueFilename);
    const wasmAsset = this.app.assets.find(module.wasmFilename);
    const fallbackAsset = this.app.assets.find(module.fallbackFilename);

    // --- prepare asset urls
    module.glueUrl = glueAsset.getFileUrl();
    module.wasmUrl = wasmAsset.getFileUrl();
    module.fallbackUrl = fallbackAsset.getFileUrl();

   //  --- run the loader
1 Like

Yes, that helped! I will post an example here for reference.

Using Assmebly Script I compiled a Typescript function into a WASM binary called math.wasm, which I then simply drag and dropped into project assets.

Typescript:

export function add(a: i32, b: i32): i32 {
  return a + b;
}

Build flags in package.json:

asc assembly/index.ts --target release --runtime none --importMemory

Playcanvas Script:

/* jshint esversion: 6 */

let Test = pc.createScript('test');
Test.extend({
    
    initialize: function() {
        
        const myMath = this.app.assets.find('math.wasm');
        const path = myMath.getFileUrl();
        
        // we only need a tiny memory for this example
        const byteSize = 8 << 1;
        const memory = new WebAssembly.Memory({ initial: ((byteSize + 0xffff) & ~0xffff) >>> 16 });
        
        WebAssembly.instantiateStreaming(fetch(path), {
            env: { memory }
        }).then((result) => {
            let lib = result.instance.exports;
            
            lib.add(1, 2);   // 3
        });
    }
    
});

Thank you, @Leonidas and @mvaligursky :slight_smile:

1 Like

nice, good test, I’m hoping to get to test moving some heavy math to AssemblyScript to see the impact.

2 Likes