@AHMED_RAPIRA

Как получить буфер линейной памяти wasm модуля из WebWorker без его копирования?

У меня есть основной поток, на стороне которого находится canvas, а также web worker, на стороне которого инициализируется wasm модуль и живет там. У wasm модуля имеется линейная память, которая хранит пиксели для дальнейшей отрисовки их на canvas снаружи. Хочу передавать эти пиксели как zero-copy для производительности

Когда я передаю буфер wasm через postMessage простым способом, происходит копирование насколько я проверил:

// код worker'a
self.postMessage({
  event: 'render',
  bytes: instance.exports.memory.buffer,
});


Я проверил так: снаружи меняю bytes, и на стороне worker эти изменения не случаются.

Затем я попробовал добавить transferrable вторым аргументом, поскольку ArrayBuffer (аля instance.exports.memory.buffer) может быть transferrable:
// код worker'a
self.postMessage({
  event: 'render',
  bytes: instance.exports.memory.buffer,
}, [instance.exports.memory.buffer]);


но получаю ошибку:
7f3f45f6-51d7-4604-bb4e-a7184c0e7651:36 Uncaught TypeError: Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope': ArrayBuffer at index 0 is not detachable and could not be transferred.
    at self.onmessage (7f3f45f6-51d7-4604-bb4e-a7184c0e7651:36:12)


Подозреваю что instance.exports.memory.buffer привязан к wasm и не может быть от него оторван.

Еще мне известен вариант с SharredArrayBuffer, но тут такое дело, память инициируется внутри wasm-бинарника, и она снаружи как бы просто как ArrayBuffer, как ее превратить в Shared не знаю, да и + поддержка же хромает у такого буфера?

Вопрос: как быть, как передать буфер наружу без копирования?
  • Вопрос задан
  • 109 просмотров
Решения вопроса 1
@desocrew
Действительно, вы не можете передать ArrayBuffer, который используется внутри WebAssembly, через postMessage() с опцией transfer. Это происходит потому, что в этом случае браузер пытается оторвать буфер от исходного контекста и присоединить его к новому контексту (в данном случае, от worker'а к основному потоку). Однако ArrayBuffer, связанный с памятью, используемой внутри WebAssembly, не может быть оторван и перенесен в другой контекст.

Есть несколько вариантов, как можно передать данные между основным потоком и worker'ом без копирования.

1. Использовать SharedArrayBuffer вместо ArrayBuffer. Однако, как вы уже заметили, у SharedArrayBuffer есть ограничения на использование в некоторых браузерах. Кроме того, не все платформы поддерживают SharedArrayBuffer (например, некоторые мобильные устройства iOS). Чтобы использовать SharedArrayBuffer в вашем случае, вы можете создать его в основном потоке и передать его в worker через postMessage(). Затем вы можете использовать этот SharedArrayBuffer внутри вашего WebAssembly модуля.

2. Вы можете использовать Transferable Objects для передачи данных без копирования. В вашем случае, если память внутри WebAssembly модуля уже инициализирована, вы можете передать указатель на начало памяти через Transferable Objects. Например:

Основной поток:
const mem = instance.exports.memory.buffer;
const ptr = instance.exports.getPixelDataPtr();
worker.postMessage({mem, ptr}, [mem]);

Web Worker:
self.onmessage = (event) => {
  const {mem, ptr} = event.data;
  const pixels = new Uint8Array(mem, ptr, numPixels * 4);
  // делаем что-то с пикселями
}

В этом случае, вы передаете указатель на начало памяти вместе с буфером, но не копируете саму память. Вы можете использовать TypedArray для доступа к данным в памяти. Обратите внимание, что в вашем WebAssembly модуле должна быть функция, которая возвращает указатель на начало памяти, который вы можете передать в worker.

Однако, если вы измените данные в памяти на стороне worker'а, эти изменения не будут отражены в памяти, используемой внутри WebAssembly модуля.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы