在微信小程序中使用 WebAssembly 调用 OpenCV
微信小程序中已经支持了 WebAssembly ,但他的文档语焉不详,也无法直接使用 opencv 编译出来的 js 胶水文件。我们需要一些简单的改动,让他可以工作。
具体流程
使用 WXWebAssembly 代替 WebAssembly
在微信小程序中,需要把所有的 WebAssembly
都替换成 WXWebAssembly
。我们可以直接 opencv 编译出的 js 胶水文件中的所有内容
替换完成后,要调整 WXWebAssembly
中没有的 API ,如 function abort()
中,有对 WXWebAssembly.RuntimeError
的引用,我们可以直接改写
- var e = new WXWebAssembly.RuntimeError(what);
- throw e
+ throw what
调整 wasm 文件的加载方式
由于微信只支持使用本地的 wasm 文件加载方式,所以我们可以调整 js 文件中的异步加载的过程,简化代码,可以解决大部分问题
在 instantiateArrayBuffer 方法中,原:
function instantiateArrayBuffer(receiver) {
return getBinaryPromise().then(function (binary) {
return WebAssembly.instantiate(binary, info)
}).then(function (instance) {
return instance
}).then(receiver, function (reason) {
err("failed to asynchronously prepare wasm: " + reason);
abort(reason)
})
}
修改后:
function instantiateArrayBuffer(receiver) {
return WXWebAssembly.instantiate("your-lib.wasm", info)
.then(function(wasm) {
receiver(wasm);
})
}
他的调用者 instantiateAsync 也可以简化成
function instantiateAsync() {
return instantiateArrayBuffer(receiveInstantiationResult)
}
删除一些微信小程序不支持的方法
微信中有许多不支持的 BOM 会被 WebAssembly 的胶水代码引用,我们都要改成替代方法,如
DB_NAME: () => {
return "EM_FS_" + window.location.pathname
},
改成
DB_NAME: () => {
return "EM_FS_WORKER"
},
还有
if (ENVIRONMENT_IS_WORKER) {
scriptDirectory = self.location.href
} else if (typeof document != "undefined" && document.currentScript) {
scriptDirectory = document.currentScript.src
}
if (scriptDirectory.indexOf("blob:") !== 0) {
scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1)
} else {
scriptDirectory = ""
}
检查以后,这里完全可以简化成
scriptDirectory = ""
另外在真机上,不支持 new Function
这样的写法,所以 createNamedFunction
就要简化成
function createNamedFunction(name, body) {
return ()=>{}
}
同理,真机没有 performance
对象,我们要替换其为 Date
方法
- } else _emscripten_get_now = (() => performance.now());
+ } else _emscripten_get_now = (() => new Date().getTime());
输出模块,更好调用
删除文件底部的 run 函数,并 export default Module
- run();
+ export default Module;
这样在调用处,我们可以这样
const wasm = require('./lib').default
wasm.onRuntimeInitialized = ()=> {
// your callback
};
wasm.run(); // start loading
使用时的事项
微信上 onCameraFrame 的方法返回的 frame.data 是一个 ArrayBuffer ,是一个 RGBA 的数据,我们在实际使用中经常会通过 Worker.postMessage
传递给 Worker 运算来提升效率。
WebAssembly 中无法直接访问到 js 空间中的内存,我们需要手动申请内存,并把 ArrayBuffer 的值传进去,这里需要一个 Uint8Array 的转化,我们才能在 WebAssembly 空间中使用
const { width, height, data } = payload;
let buffer = wasm._malloc(4 * width * height);
const newData = new Uint8Array(data);
wasm.HEAPU8.set(newData, buffer);
wasm._analyse(buffer);
// free
wasm._free(buffer);
另外,在实际使用时,由于真机的运行内存比 PC 少太多,所以我们要打开内存增涨 flag 以防止 OOM
-sALLOW_MEMORY_GROWTH=1