ОК, я нашел решение.Это может быть сложнее, чем в Rust, но для меня это работает.Я использую модуль управления памятью Wasm, чтобы вручную выделить и освободить память Wasm.Я копирую весь ImageData.data в него и обратно после завершения работы.Это делает весь процесс намного быстрее.
const go = new window.Go();
// use the same WASM memory for all Wasm instances
const memory = new WebAssembly.Memory({initial: 1024});
Promise.all([
// The main Wasm module with my photo filter
WebAssembly.instantiateStreaming(fetch('some-go-wasm-module.wasm'), {
env: {memory},
...go.importObject
}),
// the memory library written in C provides: abort, calloc, free, malloc, memcoy, memset, sbrk
// source: https://github.com/guybedford/wasm-stdlib-hack/blob/master/dist/memory.wasm
WebAssembly.instantiateStreaming(fetch("memory.wasm"), {
env: {memory}
})
])
.then(module => {
go.run(module[0].instance);
window.wasm.memHelper = {
memory,
...module[1].instance.exports
};
});
Затем я могу использовать его для выделения памяти, доступ к которой может получить моя функция Go:
const context = canvas.getContext("2d");
const size = canvas.width * canvas.height * 4;
// allocate memory for the image bitmap
const ptr = window.wasm.memHelper.malloc(size);
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
// create a new ImageData object from this memory
const dataGo = new Uint8ClampedArray(window.wasm.memHelper.memory.buffer, ptr, size);
const imageDataGo = new ImageData(dataGo, canvas.width, canvas.height);
// copy the image from JS context to the Wasm context
imageDataGo.data.set(imageData.data);
// run my Go filter
window.wasm.go.convolve_mem(ptr, canvas.width, canvas.height);
// copy the image bitmap from Wasm context back to the canvas
context.putImageData(imageDataGo, 0, 0);
// free memory
window.wasm.memHelper.free(ptr);
А сам фильтр этого не сделалтак сильно изменилось:
// somewhere in main():
// The function wich is called from JS
exports["convolve_mem"] = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
ptr := uintptr(args[0].Int())
width := args[1].Int()
height := args[2].Int()
size := width * height * 4
// Create an byte array as big as possible and create a slice with the correct size. Because we can not define a array size with non-constant variable.
data := (*[1 << 30]byte)(unsafe.Pointer(ptr))[:size:size]
matrix := []float64{
0.0, 0.2, 0.0,
0.2, 0.2, 0.2,
0.0, 0.2, 0.0,
}
benchmarks.ConvolveMem(data, width, height, matrix, 1)
return nil
})
// the filter function:
func ConvolveMem(data []byte, width int, height int, matrix []float64, factor float64) {
side := int(math.Sqrt(float64(len(matrix))))
halfSide := int(side / 2)
newData := make([]byte, width*height*4)
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
outputIndex := (y*width + x) * 4
r := 0.0
g := 0.0
b := 0.0
for cy := 0; cy < side; cy++ {
for cx := 0; cx < side; cx++ {
scy := y + cy - halfSide
scx := x + cx - halfSide
if scy >= 0 && scy < height && scx >= 0 && scx < width {
sourceIndex := (scy*width + scx) * 4
modify := matrix[cy*side+cx]
r += float64(data[sourceIndex]) * modify
g += float64(data[sourceIndex+1]) * modify
b += float64(data[sourceIndex+2]) * modify
}
}
}
newData[outputIndex] = byte(r * factor)
newData[outputIndex+1] = byte(g * factor)
newData[outputIndex+2] = byte(b * factor)
newData[outputIndex+3] = data[outputIndex+3]
}
}
copy(data, newData)
}
Теперь вся процедура немного быстрее, чем моя реализация Rust.Оба все еще медленнее, чем чистый JS.Я до сих пор не знаю почему.Но результат теперь намного лучше.