создание массива по известному адресу кучи в emscripten - PullRequest
0 голосов
/ 07 февраля 2020

Я передаю несколько килобайт данных (сгенерированный файл PNG) из контекста Unity3D WebGL в javascript, чтобы пользователь мог загрузить файл PNG, не выходя из контекста WebGL. Unity использует emscripten и встраивает js как jslib. Впервые я посмотрел на emscripten или использовал указатели в js, испытывая затруднения при поиске основ в документах emscripten.

Это работает, но я думаю, что это плохая реализация, вот код:

mergeInto(LibraryManager.library, {
    JSDownload: function(filenamePointer, dataPointer, dataLength) {
        filename = Pointer_stringify(filenamePointer);
        var data = new Uint8Array(dataLength);
        for (var i = 0; i < dataLength; i++) {
            data[i]=HEAPU8[dataPointer+i];
        }
        var blob = new Blob([data], {type: 'application/octet-stream'});
        if(window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveBlob(blob, filename);
        }
        else{
            var elem = window.document.createElement('a');
            elem.href = window.URL.createObjectURL(blob);
            elem.download = filename;        
            document.body.appendChild(elem);
            elem.click();        
            document.body.removeChild(elem);
        }
    }
});

Что беспокоит меня, так это пошагово перебирать данные, поскольку у меня уже есть адрес и длина, которую я хочу создать для массива «data» по известному адресу, как если бы я использовал * и & в C, вместо того, чтобы копировать его побайтно, или, если мне нужно скопировать его, по крайней мере, сделайте это одним ударом, а не oop. Я думаю, что моя самая большая проблема - не знать, где искать документацию. Я нашел больше от просмотра случайных проектов на GitHub, чем здесь: https://emscripten.org/docs/api_reference/preamble.js.html

Любая помощь будет оценена, спасибо.

1 Ответ

1 голос
/ 07 февраля 2020

То есть вам не нравится эта часть?

var data = new Uint8Array(dataLength);
for (var i = 0; i < dataLength; i++) {
    data[i]=HEAPU8[dataPointer+i];
}
var blob = new Blob([data], {type: 'application/octet-stream'});

Вы можете сделать ее однострочной:

var blob = new Blob([HEAPU8.subarray(dataPointer, dataPointer + dataLength)], {type: 'application/octet-stream'});

// or this

var blob = new Blob([new Uint8Array(HEAPU8.buffer, dataPointer, dataLength)], {type: 'application/octet-stream'});

Они оба должны быть намного быстрее, чем ваш исходный код, и оба они должны иметь одинаковую производительность. Это потому, что они создают новый Blob непосредственно из HEAPU8 без создания дублированного массива, как ваш исходный код.

HEAPU8 является Uint8Array, одним из семейства TypedArray. Одна действительно важная вещь в TypedArray - это то, что это на самом деле не буфер / данные, а скорее «представление» базового объекта ArrayBuffer (это HEAPU8.buffer), который содержит фактические данные. См. ArrayBufferView.

Итак, HEAPU8 предоставляет интерфейс для HEAPU8.buffer объекта ArrayBuffer, в частности WebAssembly.Memory.buffer в Emscripten, чтобы выглядеть как массив uint8_t , Emscripten также предоставляет HEAPU16, HEAPU32, HEAPF32 и др. c, но у них один и тот же ArrayBuffer с разными представлениями.

Что .subarray(start, end) и new Uint8Array(buffer, offset, size) делают для создания нового "представления" ArrayBuffer объект с указанным диапазоном, не копировать буфер. Таким образом, вы получите минимальный штраф за производительность.

...