在 emscripten 中的已知堆地址处创建数组

creating an array at a known heap address in emscripten

我将几 kB 的数据(生成的 PNG 文件)从 Unity3D WebGL 上下文传递到 javascript,以便用户可以在不离开 WebGL 上下文的情况下下载 PNG 文件。 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 中使用 * 和 & 一样,而不是逐字节复制它,或者如果我必须复制它,至少一次点击而不是循环执行。我认为我最大的问题是不知道去哪里寻找文档。我通过查看 GitHub 上的随机项目发现了比此处更多的信息:https://emscripten.org/docs/api_reference/preamble.js.html

如有任何帮助,我们将不胜感激。

所以你不喜欢这部分?

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'});

它们都应该比您的原始代码快得多,并且它们应该具有完全相同的性能。这是因为他们直接从 HEAPU8 创建了一个新的 Blob 而没有像您的原始代码那样创建重复的数组。

HEAPU8Uint8Array,属于 TypedArray 家族之一。关于 TypedArray 的一个非常重要的事情是它实际上不是 buffer/data,而是底层 ArrayBuffer(它是 HEAPU8.buffer)对象的 "view"保存实际数据。参见 ArrayBufferView

所以 HEAPU8HEAPU8.buffer ArrayBuffer 对象提供了一个接口,特别是在 Emscripten 中的 WebAssembly.Memory.buffer,看起来像一个 uint8_t 数组。 Emscripten 还提供 HEAPU16、HEAPU32、HEAPF32 等,但它们具有相同的 ArrayBuffer,但视图不同。

.subarray(start, end)new Uint8Array(buffer, offset, size)所做的是创建一个新的"view"具有指定范围的ArrayBuffer对象,而不是复制缓冲区。因此,您的性能损失最小。